As computing becomes more ubiquitous in our objects, designers need to be more aware of how to design meaningful interactions into electronically enhanced objects. At the University of Washington, a class of junior Interaction Design majors is exploring this question. These pages chronicle their efforts.

Thursday, December 12, 2019

Willo by Terrene Huang & Brys Fleming-Henning Des. 325 Fall 19'


By Terrene Huang and 
      Brys Fleming-Henning

Problem Space & Process:
We wanted to focus on providing something for gardeners in their home backyards. Though we started out unsure of what to sense on plants, we slowly shifted towards the decision of reading plant colors with the intent to detect when a plant had yellowing leaves. From there, we decided we wanted to read a broader range of colors and expanded into being able to read fruit ripeness from color. We wanted the user to be able to know information from afar, and so we chose to mount the sensor in a way where it could be moved by motors and a joystick around a plant. The lights were then a fun way for a user to understand the information from the sensor.



Testing color and RGB strip code


Testing Hoberman sphere arm



Laser cut cardboard prototype

Final Product & Components:

Structure
  • Laser cut birch
  • Laser cut acrylic
  • Assorted fasteners
  • Blue clay plant pot
  • Arduino
  • Ornamental Red Pepper plant 
Input 
  • Joystick
  • RGB Color Sensor
Processing 
  • Arduino Eno
  • Breadboard
Output
  • Continuous servo motor
  • Servo motor
  • NeoPixel LED strip
Power
  • AA Battery pack for Neo Pixel strip
  • Mini Lithium battery back for Arduino

Willo


Willo is a lamp that displays a small light show of the most prominent color read off a plant part. Willo adds a fun spectacle to the home backyard and allows for reading the ripeness of a fruit through color.

Willo consists of a laser cut wooden frame that sits against a plant pot, with the sensor hanging over the plant. The sensor can be controlled with a small joystick that allows it to circle and drop down into the plant to read its color when the joystick is pressed. Once read, the sensor detects colors in RGB and outputs the most prominent color onto a NeoPixel LED strip.

Moving the sensor is done through one continuous servo motor and one servo motor. One direction of the joystick is programmed to rotate the servo motor's arm 180 degrees then back, while the other direction rotates the continuous servo motor where indicated. Pressing the button on the joystick runs the code for the RGB sensor. Since the sensor reads wave frequencies, the values must be mapped to RGB hues instead. From there, the code detects the most prominent of color from red, green, blue, purple, and yellow. The NeoPixel strip then flashes white, before doing a light show of the detected shade of the most prominent color.



The frame design of Willo contours slightly to the specific pot's curvature using laser cut living hinges. Slots in the frame allow for the continuous servo motor to sit on top and be moved to different heights depending on plant height.


The base of the frame wraps around the bottom of the pot and utilizes the weight of the pot to hold the stand upright.





The arm holding the sensor emulates a branch of a Hoberman sphere in order to extend into the plant in a similarly curved manner.



Video:



Code:

// import libraries for servo motor and neopixel
#include <Servo.h>
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

// define pins for NeoPixel strip
#define PIXEL_PIN 12
#define PIXEL_COUNT 30

// color sensor pins wiring to Arduino
#define S0 6
#define S1 7
#define S2 8
#define S3 9
#define sensorOut 10

// create servo object
Servo servoY;
Servo servoX;

// set up NeoPixel strip mode
Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);

// set joystick in y direction, servo motor pin, and joystick y value
int joyYPin = A0;
int servoYPin = 13;
int joyY = 0;

// set joystick in x direction, servo motor pin, and joystick x value
int joyXPin = A1;
int servoXPin = 11;
int joyX = 0;

// stores frequency read by photodiodes of color sensor
int redFrequency = 0;
int greenFrequency = 0;
int blueFrequency = 0;

// stores red, green, and blue colors
int redColor = 0;
int greenColor = 0;
int blueColor = 0;

// set button pin and state
int buttonPin = 2;
int buttonState = 0;

void setup() {
  Serial.begin(9600);

  // attaches servo motor objects to respective pins
  servoX.attach(servoXPin);
  servoY.attach(servoYPin);

  // sets up NeoPixel strip, initializes strip object, initializes pixels to off
  strip.begin();

  // sets pins for ouput for color sensor and joystick
  pinMode(S0, OUTPUT);
  pinMode(S1, OUTPUT);
  pinMode(S2, OUTPUT);
  pinMode(S3, OUTPUT);
  pinMode(sensorOut, INPUT);
  digitalWrite(S0, HIGH);
  digitalWrite(S1, LOW);

  // pin mode set for button switch
  pinMode(buttonPin, INPUT_PULLUP);
  for (int i = 2; i <= 5; i++){
    pinMode(i, INPUT_PULLUP);
  }
}

void loop() {
  // moves servo motor in/out (x direction)
  joyX = analogRead(joyXPin);
  if (joyX > 0 && joyX < 511.5) {
    joyX = map(joyX, 0, 511.5, 0, 180);
  }
  if (joyX > 511.5 && joyX < 1023) {
    joyX = map(joyX, 511.5, 1023, 180, 0);
  }
  servoX.write(joyX);
  delay(15);

  // moves servo motor in/out (y direction)
  joyY = analogRead(joyYPin);
  joyY = map(joyY, 0, 1023, 0, 180);
  servoY.write(joyY);
  delay(15);

  // checks state of joystick button
  buttonState = digitalRead(buttonPin);

  // reads fruit color if button is pressed
  if (buttonState == LOW) {
    // clears NeoPixel strip when button is pressed
    strip.clear();
    strip.show();
   
    // read red
    digitalWrite(S2, LOW);
    digitalWrite(S3, LOW);
    redFrequency = pulseIn(sensorOut, LOW);
 
    // map red frequency from 0-255 with lowest and highest number from frequency reads
    redColor = map(redFrequency, 20, 142, 255, 0);
    // Serial.print("R: ");
    // Serial.print(redFrequency);
    // Serial.print(redColor);
    delay(100);

    // read green
    digitalWrite(S2, HIGH);
    digitalWrite(S3, HIGH);
    greenFrequency = pulseIn(sensorOut, LOW);
 
    greenColor = map(greenFrequency, 35, 208, 255, 0);
    // Serial.print(" | G: ");
    // Serial.print(greenFrequency);
    // Serial.print(greenColor);
    delay(100);

    // read blue
    digitalWrite(S2, LOW);
    digitalWrite(S3, HIGH);
    blueFrequency = pulseIn(sensorOut, LOW);
 
    blueColor = map(blueFrequency, 23, 183, 255, 0);
    // Serial.print(" | B: ");
    // Serial.println(blueFrequency);
    // Serial.print(blueColor);
    delay(100);

    Serial.print(redColor);
    Serial.print(" , ");
    Serial.print(greenColor);
    Serial.print(" , ");
    Serial.println(blueColor);

    // changes NeoPixel strip color to shade of most prominent color detected
    // change to red
    if(redColor > greenColor && redColor > blueColor && abs(redColor - blueColor > 50)){
      for(int i = 0; i<PIXEL_COUNT; i++){
        redColor = redColor - 30;
        colorWipe(strip.Color(127, 127, 127), 30);
        theaterChase(strip.Color(redColor, 0, 0), 70);
        break;
      }
    }
    // change to green
    else if (greenColor > redColor && greenColor > blueColor && greenColor - redColor > 30){
      for(int i = 0; i<PIXEL_COUNT; i++){
        greenColor = greenColor - 50;      
        colorWipe(strip.Color(127, 127, 127), 30);
        theaterChase(strip.Color(0, greenColor, 0), 70);
        break;
      }
    }
    // change to blue
    else if (blueColor > redColor && blueColor > greenColor && abs(redColor - blueColor) > 50 ){
      for(int i = 0; i<PIXEL_COUNT; i++){
        blueColor = blueColor - 50;
        colorWipe(strip.Color(127, 127, 127), 30);
        theaterChase(strip.Color(0, 0, blueColor), 70);
        break;
      }
    }
    // change to purple
    else if (abs(redColor - blueColor) < 50 && redColor > greenColor && blueColor > greenColor) {
      for(int i = 0; i<PIXEL_COUNT; i++){
        redColor = redColor - 20;
        blueColor = blueColor - 20;
        colorWipe(strip.Color(127, 127, 127), 30);
        theaterChase(strip.Color(redColor, 0, blueColor), 70);
        break;
      }
    }
    // change to yellow
    else if (greenColor - redColor < 30 && greenColor - redColor > 0 && greenColor > redColor && greenColor > blueColor) {
        for(int i = 0; i<PIXEL_COUNT; i++){
        redColor = redColor - 20;
        greenColor = greenColor - 20;
        colorWipe(strip.Color(127, 127, 127), 30);
        theaterChase(strip.Color(redColor, greenColor, 0), 70);
        break;
      }
    }
    // otherwise no color
    else {
      strip.Color(0, 0, 0);
      strip.show();
    }
  }
}

// sets whole strip to single color
void colorWipe(uint32_t color, int wait) {
  for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
    strip.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip.show();                          //  Update strip to match
    delay(wait);                           //  Pause for a moment
  }
}

// flashes color across strip
void theaterChase(uint32_t color, int wait) {
  for(int a=0; a<10; a++) {  // Repeat 10 times...
    for(int b=0; b<3; b++) { //  'b' counts from 0 to 2...
      strip.clear();         //   Set all pixels in RAM to 0 (off)
      // 'c' counts up from 'b' to end of strip in steps of 3...
      for(int c=b; c<strip.numPixels(); c += 3) {
        strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
      }
      strip.show(); // Update strip with new contents
      delay(wait);  // Pause for a moment
    }
  }
}

No comments:

Post a Comment