Auto Sunbath Planter
This is a kind of car carrying a flower pot in the brightest direction.
We used laser-cut wood board to make the outside of this machine, and the data of the box is also shared in this document.
Supplies
Materials:
Arduino UNO R3 * 1
Continuous servo motor (FS90R) * 2
Servo motor (FS90) * 2
Light sensor (Grove - Light Sensor) * 2
Breadboard * 1
9V battery * 1
Slide switch * 1
Marble * 1
6 cm diameter wheel for continuous servomotors * 2
Flower pot * 1 (We used 9 cm diameter and 5° H plastic pot as in the pictures)
3mm thickness 60 * 30cm plywood
Tools:
laser cutter machine
3D printer (optional)
Glue gun
Arduino Wiring
Wire an arduino like in this figure
Assembling
Printing the exterior using a laser cutter
the printing data is shared in the file
3D print the free wheel
print free wheel using a 3D printer. this data is originated from the website below
https://cults3d.com/en/3d-model/various/marble-caster
Sold slide switch
Sold the slide switch to make a connection as in STEP 1
Assemble the exterior and glue parts
Now it's the time to assemble all the parts. The main part of this machine is like in the 2nd photo. To fix 2 continuous servo motors, use 2 wooden pieces in the laser cutter part.
You will also need tubes to cover the light sensors to detect the light direction.
Code
#include <Servo.h>
Servo servoFront; // set up the servo motor in front
Servo servoRear; // set up the servo motor in rear
Servo servoRight; // set up the right servo motor
Servo servoLeft; // set up the left servo motor
int servoRight_stop = 90; // the value witch the right servo motor stops
int servoLeft_stop = 90; // the value whitch the left servo motor stops
struct Average{ // struct for the function "takeAve"
float aveFront;
float aveRear;
};
Average average;
struct GetSensor{ // Struct for the function "getSensor"
int maxAngle;
int maxLumo;
};
int sensorFront = A1; // pin for the front sensor
int sensorRear = A0; // pin for the rear sensor
int i; // use for counters
void setup() {
Serial.begin(9600);
pinMode(sensorFront, INPUT); // configure the pin for the rear sensor
pinMode(sensorRear, INPUT); // configure the pin for rear sensor
}
void loop() {
average = takeAve();
if((average.aveFront + average.aveRear)/2 < 800){ // if the machine is not in the well-lit area, start process
attachServo(); // attaching the servo motors
controlServoLR("stop"); //stop the machine
GetSensor max_angle_and_lumo = getSensor(); // call "getSensor" to get the best angle and biggest sensor data
int maxAngle = max_angle_and_lumo.maxAngle; // assign the best angle
int maxLumo = max_angle_and_lumo.maxLumo; // assign the biggest sensor data
Serial.println((String)"the max value observed in function getsensor: " + maxLumo); // print the biggest sensor data
Serial.println((String)"the angle which max value was observed in function getsensor: " + maxAngle); // print the best angle
move(maxAngle, maxLumo); // call function "move" to move the machine
detachServo(); // detaching the servo motors
}
delay(2000);
}
void attachServo(){ // attaching the servo motors
servoFront.attach(9);
servoRear.attach(10);
servoRight.attach(5);
servoLeft.attach(6);
}
void detachServo(){ // detaching the servo motors
servoFront.detach();
servoRear.detach();
servoLeft.detach();
servoRight.detach();
}
Average takeAve(){ // take averages of each sensor
int iter = 100; // iteration times for the operation to take average values
float lumoFront; // sensor value for the front sensor
float lumoRear; // sensor value for the rear sensor
float listFront[iter]; // list to store "lumoFront"
float listRear[iter]; // list to store "lumoRear"
//initialize the lists
for(i=0;i<iter;i++){
listFront[i] = 0;
listRear[i] = 0;
}
for(i=0;i<iter;i++){
// assign the sensor values
lumoFront = analogRead(sensorFront);
lumoRear = analogRead(sensorRear);
// sensor values are devide by iter for the preparation of the caluculation of the averagee value
listFront[i] = lumoFront/iter;
listRear[i] = lumoRear/iter;
delay(10); // wait for 0.01 sec
}
//take Average
float aveFront=0;
float aveRear=0;
Average takeAve;
for(i=0;i<iter;i++){
// calculate the sums of each list to get average value
aveFront+=listFront[i];
aveRear+=listRear[i];
}
// assign aveFront and aveRear to the struct (Average) takeAve
takeAve.aveFront = aveFront;
takeAve.aveRear = aveRear;
return takeAve;
}
GetSensor getSensor(){
Serial.println("Get sensor value");
int pos; // counter for the position of the sensor attached servomotors
int maxAngle = 0; // the best angle
int maxLumo = 0; // the biggest lumosity
int lumoFront; // sensor value for the front sensor
int lumoRear; // sensor value for the rear
Average ave = takeAve();
lumoFront = ave.aveFront;
lumoRear = ave.aveRear;
// search the biggest value and the best direction
if(lumoFront < lumoRear){
maxAngle = 180;
maxLumo = lumoRear;
}else{
maxAngle = 0;
maxLumo = lumoFront;
}
// assign values to struct GetSensor
GetSensor max_angle_and_lumo;
max_angle_and_lumo.maxAngle = maxAngle;
max_angle_and_lumo.maxLumo = maxLumo;
return max_angle_and_lumo;
}
void move(int maxAngle, int maxLumo){
//Rotate the machine
Serial.println((String)"the max value observed in function getsensor: " + maxLumo);
Serial.println((String)"the angle which max value was observed in function getsensor: " + maxAngle);
long threholdRotate = 0.97 * maxLumo;
// variables to store temporal sensor values
int tempLumoFront;
int tempLumoRear;
// variables to store average sensor values
int lumoFrontCenter;
int lumoFrontLeft;
int lumoFrontRight;
int lumoRearCenter;
int pos; // counter for the position of servo motor with sensor
int rotateTime = 1000;
int searchAngle = 60; // angle for searching the direction
Average average;
// Sensoring the light in front of the machine
for (pos = 0; pos <= 90; pos++) { // goes from 0 degree to 90 degrees
servoFront.write(pos); // tell servo to go to position in variable 'pos'
servoRear.write(pos);
delay(5); // waits 5 ms for the servo to reach the position
}
// rotate the machine
Serial.println("Rotate the machine");
// take temporal sensor values to decide which side to rotate
tempLumoFront = analogRead(sensorFront);
tempLumoRear = analogRead(sensorRear);
while(tempLumoFront < tempLumoRear){
if(90 < maxAngle && maxAngle <= 270){ // if the light source is coming from left side
Serial.println("Rotate anti clockwise");
controlServoLR("rotateAntiClock"); // turn left
}else{ // Otherwise
Serial.println("Rotate clockwise");
controlServoLR("rotateClock"); // turn right
}
// take temporal sensor values
tempLumoFront = analogRead(sensorFront);
tempLumoRear = analogRead(sensorRear);
delay(100);
}
controlServoLR("stop");
// if the machine is in the enough light source, does not move
// take average values of the sensors
average = takeAve();
lumoFrontCenter = average.aveFront;
lumoRearCenter = average.aveRear;
// compare the value in front sensor whie center
while(true){
// take the value of the front sensor
average = takeAve();
lumoFrontCenter = average.aveFront;
lumoRearCenter = average.aveRear;
// take a average of the left front
for (pos; pos >= 90 - searchAngle; pos--) { // goes from 0 degree to 90 degrees
servoFront.write(pos); // tell servo to go to position in variable 'pos'
delay(5); // waits 5 ms for the servo to reach the position
}
average = takeAve();
lumoFrontRight = average.aveFront;
// take average value for the right front
for (pos; pos <= 90 + searchAngle; pos++) { // goes from 0 degree to 90 degrees
servoFront.write(pos); // tell servo to go to position in variable 'pos'
delay(5); // waits 5 ms for the servo to reach the position
}
average = takeAve();
lumoFrontLeft = average.aveFront;
// reverse the servomotor
for (pos; pos >= 90; pos--) { // goes from 0 degree to 90 degrees
servoFront.write(pos); // tell servo to go to position in variable 'pos'
delay(5); // waits 5 ms for the servo to reach the position
}
if (lumoFrontCenter < lumoRearCenter * 0.7 || lumoFrontLeft < lumoRearCenter * 0.7 || lumoFrontLeft < lumoRearCenter * 0.7){ // when the sensor value of front is the biggest but smaller than the the value of the rear sensor
if (lumoFrontLeft < lumoFrontRight){ // if the right side is brighter
controlServoLR("rotateClock"); // rotate clockwise
}else{ // if the left side is brighter
controlServoLR("rotateAntiClock"); // rotate anti clockwise
}
while(true){ // rotate until the sensor value of the front being twice bigger than the rear one
// take temporal sensor values
tempLumoFront = analogRead(sensorFront);
tempLumoRear = analogRead(sensorRear);
delay(rotateTime/10);
if(tempLumoFront > tempLumoRear * 2){
break;
}
}
}else if(lumoFrontCenter < lumoFrontLeft && lumoFrontRight < lumoFrontLeft){ // the light from rear is small enough, although the sensor value from left side is bigger than center one
controlServoLR("rotateAntiClock");
delay(rotateTime);
}else if(lumoFrontCenter < lumoFrontRight){ // the light from rear is small enough, although the sensor value from right side is bigger than center one
controlServoLR("rotateClock");
delay(rotateTime);
}else{ // when the sensor value of the front sensor is the biggest
break;
}
controlServoLR("stop");
}
controlServoLR("stop");
delay(1000); // wait for 1 sec
// moving forward up to 3 sec, stop if the machine is going into shade
int tempSensorValFront = 0; // the temporal sensor value, initialize with 0
int countShade = 0;
float thresholdNotMoving = 0.05; // the threshold persentage for the machine stacked somewhere
int countNotMoving = 0;
Serial.println("Start moving up to 3 sec");
// start moving forward
controlServoLR("forward");
for(i=0;i<100;i++){ // for loop for 100 times (5 sec)
tempLumoFront = analogRead(sensorFront); // assign sensor value to tempLumoFront
tempLumoRear = analogRead(sensorRear);
if(tempLumoFront < tempSensorValFront * 0.5 || tempLumoFront * 2 < tempLumoRear){ // stop moving if the machine goes to shade
countShade += 1;
if (countShade == 1){
Serial.println("the machines stops due to the decline of light");
break; // leave the for loop
}
}/*else if(tempSensorValFront * (1-thresholdNotMoving) < tempLumoFront && tempLumoFront < tempSensorValFront * (1+thresholdNotMoving)){
countNotMoving += 1;
if (countNotMoving == 20){
Serial.println("the machine seems stacked, move backwords");
// moving back for 1 sec
controlServoLR("back"); // get away from stacking
delay(1000);
controlServoLR("rotateClock"); // move randomly
delay(1000);
break; // leave the loop
}
}*/
tempSensorValFront = tempLumoFront; // assign the front sensor value to the temporal sensor value
delay(50); // keep going for 0.5 sec
}
// stop moving the machine
controlServoLR("stop");
// move the machine backwards for 1 sec to get a enough space for the next operation
controlServoLR("back");
delay(1000);
controlServoLR("stop");
// reverse the sensor
for (pos = 90; pos >= 0; pos--) { // goes from 90 degrees to 0 degree
servoFront.write(pos); // move servoRear for the position "pos"
servoRear.write(pos);
delay(5); // waits 5 ms for the servo to reach the position
}
}
void controlServoLR(String control){ // function for control the movig of the machine
if(control == "forward"){
servoRight.write(servoRight_stop-15);
servoLeft.write(servoLeft_stop+10);
}else if(control == "stop"){
servoRight.write(servoRight_stop);
servoLeft.write(servoLeft_stop);
}else if(control == "back"){
servoRight.write(servoRight_stop+10);
servoLeft.write(servoLeft_stop-10);
}else if(control == "rotateClock"){
servoRight.write(servoRight_stop+10);
servoLeft.write(servoLeft_stop+10);
}else if(control == "rotateAntiClock"){
servoRight.write(servoRight_stop-10);
servoLeft.write(servoLeft_stop-10);
}else{
Serial.println("type 'forward', 'stop', 'back','rotateClock' or 'rotateAntiClock' please ");
}
}