This class project stems from collaborating with Osher students, who are people that lie in the mature age group, to implement a useful device for their daily lives. Our device is designed to help Beth clean-up and beautify her vast yard. Here are links to the preliminary stages in our design cycle for more context.https://courses.ideate.cmu.edu/60-223/s2020/work/interview-with-beth/

Team Beth Prototype Documentation

What we built

Our moss spreading device is a simple design. It is a remote-controlled car with a blender on the top, a tank to hold moss, and a funnel to dispense the mixture at the bottom. The purpose of the car is to cover land with a beautiful, even layer of moss with minimal effort.

Car Model

isometric view

side view

top view

 

front view

Controller Model

Controller Exploded View

Controller top view

 

TinkerCad

Each button sends a signal to the IR receiver, represented by the remote in Tinkercad.

(Subtitles may be covered by the bottom bar)

Movement demo:

Blend/dispense demo:

 

After some time, Beth sees that her yard is looking a little dull so she feels like beautifying it. Some of it is covered in moss and some is not. She does some research and finds that she can make moss by blending buttermilk and moss. The only problem is that her yard is extremely huge and complex with random patches of open area mixed with sporadic trees. It’s not convenient to only have one blender-full of moss at a time to cover 3 acres of land.

She grabs her remote controlled robot, places it in her yard and flips the power toggle to the on position. Beth begins to fill up the car’s tank by blending her moss and buttermilk with the blend button on the remote. After two full blenders, Beth starts to drive the robot by pressing the directional buttons on the remote and also pressing the dispense button so she could spread the moss mix however she would like.

How we got here

Vehicle design process

Here is our original idea for moving the fluid in the blender to the tank. It consisted of have a servo move a thin disk in and out of the nozzles. The servo would move the disk in while we didn’t need any fluid to be transferred. It would move the disk out of the way to let fluid through. We very slowly (but surely) realized that this solution wouldn’t work quite well due to the placement of the motor and how thin the disk would have to be.

Brainstorming using servomotor to open blender to tank

We then came up with a slightly better solution that would allow for easier placement of the servo and less spillage.

 

However, a problem we encountered right after was figuring out how to dispense the fluid that was transferred into the tank onto the ground without a pump. Here is picture showing a picture of our primary tank shape, which was close to a rectangular prism.

rectangular shaped tank

We were already trying to build to minimize weight which meant adding another motor for a pump would be costly. We then decided to sacrifice some efficiency with holding fluid and make the tank angled so that gravity could push out the fluid.

drawings for gravity powered tank

final tank design

 

One big problem we had to address was the dispensing mechanism.  We had to take into account leaking a mixture of thick-milkshake-like consistency, the weight of the liquid, and space allocation in the robot. A few ideas we had was a way to use magnets to open and close a door, gears that would use DC motors to bring a door up and down (like a garage door), and ways to use servos to pull a plane up and down. However, none of these ideas really solved the problem of massive waste while opening and closing.

Drawbridge style idea

 

Our solution was to mimic the blender to tank design from earlier and spread the liquid with a funnel-shaped output.

funnel design for spread

 

The biggest breakthrough decision we made was to focus on moss dispensing; our original idea was to also attach a cordless leaf blower. The weight of a strong enough leaf blower added on to the weight of the battery and all the liquid in the tank would make the robot need a lot of power and super strong motors. Beth shared this concern and pointed us in the right direction.

Our very first design, original plan to have leaf blowing moss robot

moss dispenser only iteration

 

Controller design process

The controller was initially designed to be held with both hand, inspired by Nintendo controller.

first controller iteration

However, after the feedback from Beth that she needs to spare a hand for other tasks, we changed the design to one-hand controller and made sure that the form can fit nicely in one hand.

second controller iteration

 

Work ethic and pace

Here is a schedule we had set for each other to get all the components of the project done.

set work schedule

We all stayed on track and it was because we spread out the tasks evenly and keep in mind what would take longer. It also helps that we made our tasks modular. Once one task was done it served as a bolster to completing the following task.

Conclusions and lessons learned

A question was brought up about the cleaning of this device: “If there is buttermilk left inside then there will be mold forming inside after a while. Will that affect future usage of the machine?” To clean the device, Beth would fill up the blender with water, blend, and let the water run through the tank. She could run water through until the dispensed water is clean.

Another comment was: “Have you thought about making the car solar power?” This is a great idea. To not add to the weight of the robot itself, we can make a solar powered charging station. But since Beth said she doesn’t get much sun, we would still have the AC adapter option.

A lot of comments were about testing:

“When the blender is full is the device top heavy and unstable?”

“How are you ensuring that your motors will be able to carry the weight of the entire device + when it’s full?”

This is a clever design, however I worry about the vibration of the blender and how that will impact other components of your moss rover.”

The vibration of the blender is actually something that we didn’t think about. It is a valid concern that would require testing to address. We think it was unfortunate that we couldn’t test these problems but if we find the right parts, like a strong battery and DC motors, our design would be effective. If we build up a physical prototype, Beth said we can head out to her yard and test out the device and we can answer these questions then. Overall, working with Beth was a lot of fun. It was cool that we were able to build a device for a very unique problem.

We had several Zoom meetings to figure out the mechanism together (as shown in all those sketchy sketches of the robot model above). These meetings were very fruitful and effective in coming up with theoretical solutions for some technical difficulties, such as how to design the stoppers and funnels. However, because we were working remotely, we didn’t have the opportunity to test out the area our tank could cover and how effective different batteries, motors, blenders, and wheels would be. Also we were unable to test the stoppers between the blender and tank and the funnel which are important parts of our design. Also because we couldn’t meet, we divided the remote work quite clearly and were each responsible for a certain part. In retrospect, this was effective but overall, communication, about the form, electronics, and prototyping, would have been much easier in person.

We learned how to balance ambition with practicality during our prototyping process. Our first iteration, the leaf blowing moss dispenser, was a really clever idea to us, but Beth was concerned about the practicality. So for our second iteration, we made our scope more realistic. In retrospect, we should have made our process more modular from the start. If we broke down our ideas into smaller components in the beginning and made a Gantt-chart-like plan, we would have been able to see how realistic it actually was.

The biggest takeaway from our experience working with an elderly person was that checking in with Beth during our second iteration prototyping process might have helped improve our design. As we approached the end of the project, we got very invested in how the mechanism works for the robot since we were dealing with a fairly complex system. While such investigation is very valuable and we each learned a lot from the prototyping process, we put aside the need of Beth, who was supposed to be the main focus of our design. From our previous conversations with Beth on our first idea, she pointed out how some actual circumstances were different from our assumptions so we needed to take into those real-life factors into considerations, which was one of the biggest drive for us to shift to our second iteration, making our machine less ambitious and more simplistic and functional. Similarly, it would be nice if we talk to Beth to get some feedback for our second iteration, which might push our project to an even more practical, more human-centered design result.

Technical details

Robot electronics:

https://www.tinkercad.com/things/1E1FraSAW9K

Breadboard image robot

Remote electronics:

https://www.tinkercad.com/things/4zUE3ML0yFY

Breadboard image remote

RC car schematic

Remote schematic

#include <IRremote.h>
#include <Servo.h>

/* Beth's Moss Dispenser
 *  
 *  Description: A remote controlled moss blender and dispenser to help Beth with her
 *  yard beautifying processes.
 *  
 *  https://www.tinkercad.com/things/1E1FraSAW9K
 *  
 * Pin mapping:
 * 
 * pin   | mode   | description
 * ------|--------|------------
 * 11     input     IR receiver   
 * 3      output    Left Front Motor Input 1  
 * 4      output    Left Front Motor Input 2
 * 5      output    Right Front Motor Input 1  
 * 6      output    Right Front Motor Input 2 
 * 7      output    Left Back Motor Input 1  
 * 8      output    Left Back Motor Input 2
 * 9      output    Right Back Motor Input 1  
 * 10     output    Right Back Motor Input 2 
 * A5     output    Blender to tank servomotor
 * A4     output    Dispensing door servomotor
 * A3     output    Blender Motor Input 1
 * A2     output    Blender Motor Input 2
 * 
 * 
 * Credit: https://www.instructables.com/id/Arduino-Modules-L298N-Dual-H-Bridge-Motor-Controll/
 * 
*/


const int RECV_PIN = 11;
const int LFIN1 = 3;
const int LFIN2 = 4;
const int RFIN1 = 5;
const int RFIN2 = 6;
const int LBIN1 = 7;
const int LBIN2 = 8;
const int RBIN1 = 9;
const int RBIN2 = 10;
const int BLEND2TK = A5;
const int DOOR = A4;
const int BLENDIN1 = 12;
const int BLENDIN2 = 13;


Servo blenderservo;
Servo doorservo;
unsigned int blendtimer = -20000;

unsigned int lastPress = 0;

bool dispensing = false;

bool on = true;

//IR Library stuff
IRrecv irrecv(RECV_PIN);
decode_results results;

void setup()
{
  pinMode(LFIN1, OUTPUT);
  pinMode(LFIN2, OUTPUT);
  pinMode(RFIN1, OUTPUT);
  pinMode(RFIN2, OUTPUT);
  pinMode(LBIN1, OUTPUT);
  pinMode(LBIN2, OUTPUT);
  pinMode(RBIN1, OUTPUT);
  pinMode(RBIN2, OUTPUT);
  pinMode(BLENDIN1, OUTPUT);
  pinMode(BLENDIN2, OUTPUT);

  blenderservo.attach(BLEND2TK);
  doorservo.attach(DOOR);
  
  Serial.begin(9600);

  irrecv.enableIRIn(); 
  
  blenderservo.write(0);
  doorservo.write(0);
}

void moveforward() {
  digitalWrite(LFIN1, HIGH);
  digitalWrite(LFIN2, LOW);
  digitalWrite(LBIN1, HIGH);
  digitalWrite(LBIN2, LOW);
  
  digitalWrite(RFIN1, HIGH);
  digitalWrite(RFIN2, LOW);
  digitalWrite(RBIN1, HIGH);
  digitalWrite(RBIN2, LOW);
}

void movebackward() {
  digitalWrite(LFIN1, LOW);
  digitalWrite(LFIN2, HIGH);
  digitalWrite(LBIN1, LOW);
  digitalWrite(LBIN2, HIGH);
  
  digitalWrite(RFIN1, LOW);
  digitalWrite(RFIN2, HIGH);
  digitalWrite(RBIN1, LOW);
  digitalWrite(RBIN2, HIGH);
}

void turnleft() {
  //left wheels backwards
  digitalWrite(LFIN1, LOW);
  digitalWrite(LFIN2, HIGH);
  digitalWrite(LBIN1, LOW);
  digitalWrite(LBIN2, HIGH);

  //right wheels forwards
  digitalWrite(RFIN1, HIGH);
  digitalWrite(RFIN2, LOW);
  digitalWrite(RBIN1, HIGH);
  digitalWrite(RBIN2, LOW);
}

void turnright() {
  //left wheels forwards
  digitalWrite(LFIN1, HIGH);
  digitalWrite(LFIN2, LOW);
  digitalWrite(LBIN1, HIGH);
  digitalWrite(LBIN2, LOW);

  //right wheels backwards
  digitalWrite(RFIN1, LOW);
  digitalWrite(RFIN2, HIGH);
  digitalWrite(RBIN1, LOW);
  digitalWrite(RBIN2, HIGH);
}

void blend() {
  digitalWrite(BLENDIN1, HIGH);
  digitalWrite(BLENDIN2, LOW);
  Serial.println("blending");
}


// volume and skip buttons move robot front, back, left, right
// 0 button dispenses moss
// 1 blends
// on/off button

void remoteaction() {
  unsigned int value = results.value;
  Serial.println(value);
  
  switch (value) {
    case 32895: {
      Serial.println("moving forward");
      moveforward();
      break;
    }
      
    case 36975: {
      Serial.println("moving backward");
      movebackward();
      break;
    }
      
    case 8415: {
      Serial.println("turning left");
      turnleft();
      break;
    }
      
    case 24735: {
      Serial.println("turning right");
      turnright();
      break;
    }
      
    case 12495: {
      Serial.println("dispensing moss");

      if (!dispensing) {
        doorservo.write(90);
        dispensing = true;
        break;
      }
      else {
        doorservo.write(0);
        dispensing = false;
        break;
      }
    }
    
    case 16580863: {
      Serial.println("on/off");

      if (on) on = false;
      else on = true;
      break;
    }
      
    case 2295: {
      Serial.println("start blending");
      blend();
      blendtimer = millis();

      break;
    }
    
      
  }
}

void loop() {
  // blend for 20 seconds (simulation different)
  if (millis() - blendtimer <= 20000) {
    blend();
  }
  // open blender to tank for 20 seconds then close (simulation different)
  if (millis() - blendtimer > 20000 && millis() - blendtimer < 40000) {
    blenderservo.write(90);
    digitalWrite(BLENDIN1, LOW);
    digitalWrite(BLENDIN2, LOW);
  }
  else {
    blenderservo.write(0);
    digitalWrite(BLENDIN1, LOW);
    digitalWrite(BLENDIN2, LOW);
  }

  // get remote button presses
  if (irrecv.decode(&results)) {
    Serial.println(results.value);
    
    remoteaction(); 
    if (!on) {
      Serial.println("off");
      digitalWrite(LFIN1, LOW);
      digitalWrite(LFIN2, LOW);
      digitalWrite(LBIN1, LOW);
      digitalWrite(LBIN2, LOW);
      digitalWrite(RFIN1, LOW);
      digitalWrite(RFIN2, LOW);
      digitalWrite(RBIN1, LOW);
      digitalWrite(RBIN2, LOW);
      digitalWrite(BLENDIN1, LOW);
      digitalWrite(BLENDIN2, LOW);
      blenderservo.write(0);
      doorservo.write(0);
      
    }
    
    irrecv.resume(); // Receive the next value
  }
  
}
/*
 * Project: Remote for Beth's Moss Dispenser
 * 
 * Description: Remote for moss dispenser
 * Imagine the Tinkercad Circuit was built using an Arduino Nano
 * 
 * https://www.tinkercad.com/things/4zUE3ML0yFY
 * 
 * Pin mapping:
 * 
 * pin   | mode       | description
 * ------|--------    |------------
 * 5      input pullup   move forward   
 * 2      input pullup   turn left 
 * 8      input pullup   turn right
 * 4      input pullup   move backward
 * 6      input pullup   blender
 * 7      input pullup   dispense door
 * 9      input pullup   on/off
 * 
 * 
 * Credit:
 * /https://www.arduino.cc/en/Tutorial/Debounce
 * https://beatyourbit.wordpress.com/2018/02/05/arduinoinfrared/
 * https://www.instructables.com/id/IR-Transmitter-and-Receiver-Using-Arduino/
 */

#include <IRremote.h>

IRsend irsend;

const int FRONT = 5;
const int LEFT = 2;
const int RIGHT = 8;
const int BACK = 4;
const int DOOR = 6;
const int BLENDER = 7;
const int ON = 9;


unsigned long lastFrontTime = 0;
unsigned long lastBackTime = 0;
unsigned long lastLeftTime = 0;
unsigned long lastRightTime = 0;
unsigned long lastBlenderTime = 0;
unsigned long lastDoorTime = 0;
unsigned long lastOnTime = 0;

unsigned long debounceDelay = 50;

int lastFrontState = HIGH; 
int lastBackState = HIGH; 
int lastLeftState = HIGH; 
int lastRightState = HIGH; 
int lastBlenderState = HIGH;
int lastDoorState = HIGH; 
int lastOnState = HIGH;

int frontState;
int backState;
int leftState;
int rightState;
int blenderState;
int doorState;
int onState;



void setup()
{
  pinMode(LEFT, INPUT_PULLUP);
  pinMode(RIGHT, INPUT_PULLUP);
  pinMode(BACK, INPUT_PULLUP);
  pinMode(FRONT, INPUT_PULLUP);
  pinMode(DOOR, INPUT_PULLUP);
  pinMode(BLENDER, INPUT_PULLUP);
  pinMode(ON, INPUT_PULLUP);
  
  Serial.begin(9600);
  
}

void loop()
{
  bool frontReading = digitalRead(FRONT);
  bool backReading = digitalRead(BACK);
  bool leftReading = digitalRead(LEFT);
  bool rightReading = digitalRead(RIGHT);
  bool doorReading = digitalRead(DOOR);
  bool blenderReading = digitalRead(BLENDER);
  bool onReading = digitalRead(ON);
  

  if (frontReading != lastFrontState) {
    lastFrontTime = millis();
  }
  if (backReading != lastBackState) {
    lastBackTime = millis();
  }
  if (leftReading != lastLeftState) {
    lastLeftTime = millis();
  }
  if (rightReading != lastRightState) {
    lastRightTime = millis();
  }
  if (blenderReading != lastBlenderState) {
    lastBlenderTime = millis();
  }
  if (doorReading != lastDoorState) {
    lastDoorTime = millis();
  }
  if (onReading != lastOnState) {
    lastOnTime = millis();
  }
  
  //checking for new press of forward button
  if ((millis() - lastFrontTime) > debounceDelay) {

    if (frontReading != frontState) {
      frontState = frontReading;
      
      if (frontState == LOW) {
        Serial.println("moving forward");
        irsend.sendNEC(0x0000807F, 32);
      }
    }
  }
  
  if ((millis() - lastBackTime) > debounceDelay) {

    if (backReading != backState) {
      backState = backReading;
      
      if (backState == LOW) {
        Serial.println("moving backward");
        irsend.sendNEC(0x0000906F, 32);
      }
    }
  }
  
  
  if ((millis() - lastLeftTime) > debounceDelay) {

    if (leftReading != leftState) {
      leftState = leftReading;
      
      if (leftState == LOW) {
        Serial.println("turning left");
        irsend.sendNEC(0x000020DF, 32);
      }
    }
  }
  
  if ((millis() - lastRightTime) > debounceDelay) {

    if (rightReading != rightState) {
      rightState = rightReading;
      
      if (rightState == LOW) {
        Serial.println("turning right");
        irsend.sendNEC(0x0000609F, 32);
      }
    }
  }
  
  
  if ((millis() - lastBlenderTime) > debounceDelay) {

    if (blenderReading != blenderState) {
      blenderState = blenderReading;
      
      if (blenderState == LOW) {
        Serial.println("blending");
        irsend.sendNEC(0x000008F7, 32);
      }
    }
  }
  
  if ((millis() - lastDoorTime) > debounceDelay) {

    if (doorReading != doorState) {
      doorState = doorReading;
      
      if (doorState == LOW) {
        Serial.println("dispense button");
        irsend.sendNEC(0x000030CF, 32);
      }
    }
  }
  
  if ((millis() - lastOnTime) > debounceDelay) {

    if (onReading != onState) {
      onState = onReading;
      
      if (onState == LOW) {
        Serial.println("turn on/off");
        irsend.sendNEC(0x00000000, 32);
      }
    }
  }
  
  lastFrontState = frontReading;
  lastBackState = backReading;
  lastLeftState = leftReading;
  lastRightState = rightReading;
  lastBlenderState = blenderReading;
  lastDoorState = doorReading;
  lastOnState = onReading;
}