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 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.


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.
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