The most successful Tinkercad circuit we built, for testing and learning the LCD screen and the LiquidCrystal library. See below for our full Tinkercad circuit and why it didn’t work.
To begin this assignment, we discussed various ideas of how we could transduce orientation to light. After a couple hours of excited general brainstorming, we found it very helpful to use then maker cards to narrow down our conversation in terms of input and output. This focus allowed us to see what was necessary in the middle steps of the operation , and what parts may lend themselves well to working together.
An overview of our 3 more refined ideas
Our favorite of the 0ur 3 more refined ideas was the following:
A potentiometer sets the angle for a servo for a stepper motor. A cable sticking out from the motor turns with the motor and glides on a layer of graphite drawn around the motor. A current is always flowing through the graphite, and depending on the position of the cable along the graphite trail, it’ll measure more or less current. Essentially like a homemade potentiometer whose position is determined by a stepper motor. Depending on the current read by the cable, an LED lights up more or less.
Jubbies’ concept sketch for our idea
We had gotten some good feedback from our professor as well, since this idea seemed to be a good balance between exciting and feasible. To proceed forward, we broke down our problem into several steps so we could focus on individual components before be assembled them together . The first step we needed to confirm was working independently was the one that was most unfamiliar – using a graphite resistor. To learn how to do this, Jubbies used YouTube to find a concise demonstration of this idea.
The following video was very helpful as it demonstrated some of the affordances of using graphite circuit .
Using this video as guidance, Jubbies recreated this graphite circuit with alligator clips in a 9-volt battery. This demonstration proved successful, although there was not as much variance in the light as was desired. We found that this issue was resolved as we eventually used a longer graphite resistor.
Demonstration of the graphite circuit
After proving that the graphite circuit could work, the next issue to tackle was controlling the stepper motor accurately with a potentiometer. This pursuit proved to be far more difficult than we first predicted. A huge barrier that we had to figure out how to overcome Rose making the stepper motor turn the correct direction. For some reason the stepper motor seems to only cumulatively turn to the right no matter the direction that the potentiometer was turned. it was as if the stepper motor turn only two the amount that was equivalent to the total value that the potentiometer was turned , regardless of whether that value was left or right. We tried editing the code and checking our wiring for hours, but none of this seemed to help.
here’s an example of the potentiometer moving to stepper motor only to the right depsite the direction that the potentiometer turned.
It wasn’t until receiving help directly from our professor that we were able to proceed forward. The thing that fixed this issue was in the end, simply replacing both the stepper motor and a stepper motor driver. We have not yet identified what the issue was particularly, but using the new parts with the same code achieve the results that we had wanted.
finally our stepper motor turned right AND left!!
While this issue was frustrating and strange, it did allow us to use a stepper motor that was far more robust than the one we were using before. This helped with the construction of our circuit since we did not have to worry that the arm would not move quick enough. however oh, I believe the use of outside power source led to the following image – Jubbies accidentally blew up an LED while accidentally connecting the power and ground of the LED without the graphite circuit in-between. This was a good lesson, as it reminded her to be very conscientous of these wires not connecting again.
Another strange issue that was not ideal but did not hinder the intended mechanism was an ever present jitter in the stepper motor arm. This jitter was likely due to analog interference and could be resolved in the future with a threshold or other tool to smooth the data recieved by the potentiomenter. I had considered using a rotary encoder mid-project over the potentiometer as I heard that it was less susceptible to analog interference, but I was unable to quickly implement this and ended up just using the potentiometer.
Bonus: I strapped a pen onto the stepper motor arm and found this jitter made for an interesting drawing tool to be explored further in the future – Jubbies
Carlos’s progress
My (Carlos’s) Tinkercad code somehow became ridden with invisible special characters \302 and \240, maybe after copying and pasting the code from iMessage. They were nearly impossible to remove without retyping those lines word for word all over again. Because of this, the code wouldn’t compile, and I couldn’t check if the rest of the code was correct.
Tinkercad does not have a stepper motor, so I used the larger, grey DC Motor with Encoder as a mere placeholder, not even connected to anything.
Block Diagram & Schematic:
/* 60-223, Double Transducer: The Convoluted Potentiometer Jubbies Steinweh-Adler Carlos Ortega Collaboration and sources: 1) I collaborated with Carlos to figure out the order of the code 2) Referenced heavily Circuit Digest stepper motor with Potentiometer https://circuitdigest.com/microcontroller-projects/stepper-motor-control-with-potentiometer-arduino 3) The maker cards really helped me understand the parts https://nsfsmartmakerspaces.github.io/physcomp/parts/4005/#starter-code--connection 4) I referenced Household Hacker's youtube video about paper and graphite circuits to get started with this idea. 5) Tate gave me some advice on how to use the LCD display since I could not figure it out. Challenge(s): Remote collaboration was difficult, as was working through unknown bugs with the Stepper Motor. The stepper motor and driver did not work and simply had to be replaced. Additionally, I could not get the LCD working in time. Next time: It was extremely helpful to segment the process of solving this code, proving that each mechanism works independently of the whole rig. I will certainly do this again as it really sped things up and made them less stressful. In terms of issues, I think I should have given more time to the physical assembly of all the parts, since this took way longer than I thought. I spent a lot of time trying to fashion non-destructive mounts. It was hard to physically arrange the parts together in a semi-elegant way. I also want to give more time to the small final details, like resolving the stepper motor jitter. Another thing I want to take forward from this project was using a clearly understood visual indicator of the process like the graphite semicircle, since I found this helped people make sense of this project quickly. Description: Pin mapping: Arduino pin | type | description ------------|------------|------------- A0 Input Potentiometer input 12 LCD Pin Used for the LiquidCrystal Library 11 LCD Pin Used for the LiquidCrystal Library 2 Input Stepper Motor driver 3 Input Stepper motor driver, direction 7 LCD Pin used for the LCD 6 LCD Pin used for the LCD 5 LCD Pin used for the LCD 4 LCD Pin used for the LCD */ //Initialize the LCD #include <LiquidCrystal.h> //include the LCD library code // initialize the library by associating any needed LCD interface pin // with the arduino pin number it is connected to const int rs = 12, en = 11, d4 = 7, d5 = 6, d6 = 5, d7 = 4; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); //Initialize the Stepper #include <AccelStepper.h> // including the accelstepper library for more control of motor const int POTPIN = A0; //potentiometer input pin const int STEP_PIN = 2; // A4988 "STEP" pin wired to Arduino pin 2 (you can change this) const int DIR_PIN = 3; // A4988 "DIRECTION" pin wired to Arduino pin 3 (you can change this) int pos = 100; // variable to store motor position instruction, 100 is half rotation // Modified from: //https://courses.ideate.cmu.edu/60-223/s2021/tutorials/stepper AccelStepper stepper(1, STEP_PIN, DIR_PIN); // Will compare this to currStepMotPos later, inside loop. int prevStepMotPos = 0; unsigned long timerVar = 0; const int WAIT = 200; //does not change int LCDupdate = 0; void setup() { int BAUD = 9600; //serial comm value int maxSpeedVar = 100; //measured in steps per second int accel = 500; //measured in steps per second Serial.begin(BAUD); //begin serial communication //---LCD Setup--- lcd.begin(16, 2); // Modified from: // https://nsfsmartmakerspaces.github.io/physcomp/parts/4005/#starter-code--connection //---Stepper Motor setup--- stepper.setMaxSpeed(maxSpeedVar); // measured in steps per second stepper.setAcceleration(accel); // measured in steps per second squared // while there is still a distance to go, stepper.run(); // make the motor run } void loop() { unsigned long currentTime = millis(); unsigned long previousTime = 0; } int inLo = 0; //lowest value of potentiometer range int inHi = 1023; //max value of potentiometer range int outLo = 0; //lowest value of stepper motor range int outHi = 100; //200 per rev, so half revolution int potPos = analogRead(POTPIN); //potPos assigned position of potentiometer //map pot pos range to stepper motor range int long currStepMotPos = map(potPos, inHi, inLo, outLo, outHi); //Change destination of stepper to potentiometer input stepper.moveTo(currStepMotPos); //tell motor to go to new position prevStepMotPos = currStepMotPos; //save current value into previous value stepper.run(); //Serial.println(prevStepMotPos); //for debugging LCDPotVal = LCDStepperVal = //LCD Update /*if (currentTime - timerVar >= WAIT) { lcd.clear(); lcd.home(); lcd.print("i:"); lcd.print(LCDPotVal0; lcd.setCursor(6, 0); lcd.print("m:"); */ previousTime = currentTime; }
Discussion:
This project was an exciting introduction to many new concepts such as assembling separate components into one operation, using graphite as a resistor, learning how to debug in context, soldering, and collaborating remotely. Since we were both excited about this idea, it was easier to push through these bugs. Learning hands-on was a great experience , since somebody can tell you what it’s like to explode an LED, but before you actually do it, I would argue that it is far less likely to sticks with you.
The biggest barriers we faced had to do with significant bugs and remote collaboration. The stepper motor/ stepper motor driver bug , although circumnavigated, was never resolved. Only with the help of our professors are we able to move on from that point through replacing our parts.
Additionally, since Carlos is studying remotely and never received his Arduino kit from customs, there was simply not an option to collaborate on the hardware issues. Since the software and hardware are delicately intertwined, this also made it extremely difficult to collaboratively resolve software issues as well. While on paper, tinkercad seems to be a good option to ease this situation, the lack of a stepper motor, stepper motor driver, and graphite resistor on the platform, as well as the inability to import libraries, made this tool unhelpful.
That being said, we were able to have rewarding discussions about conceptual development and enjoyed the process of translating our idea to a real tangible thing.
]]>This simulation helped us plan our physical project. Here we have a potentiometer as the input, an RGB LED and photo-resistor as our middle step, and a pancake vibration motor as our output.
Dani’s final project
Demonstration:
Nish’s Final Project image
Demonstration:
The RGB LED and the Color Sensor (purple chip) act as our middle transduction step
The accelerometer is the input to detect position change
The user holds a position sensor and can move and tilt it in all directions. This will make and LED light up in different colors, depending on the position of the sensor. A color sensor picks up what color the LED is at that moment. The color sensor will then determine how much to vibrate the output based on what color it sees.
I thought the power/ground connections went all the way to both sides of the board, but there is a small gap at the bottom. It took me a while to realize I needed to bridge the gap to close the electrical connection.
We were getting negative values on our LCD and we realized it was because we were mapping to incorrect ranges that were causing values outside of the 0-99 range.
I wasn’t able to get the LCD to display correctly for a while. It turned out the wires weren’t matched up exactly to how I had described them in the Arduino code, and the LCD pins were not fully inserted into the breadboard. It was difficult to get the pins to stay in consistently. – Nish
I was having some difficulty soldering because I decided to solder in the RGB LED first. However, this was a bad idea as it made the rest of the board uneven for me to solder the resistors and other components on. Therefore, a lot of the resistors look uneven and like they’re sticking out, as demonstrated in this picture. Also, even though it doesn’t really matter, I was upset that I had soldered upside down on the protoboard. – Nish
The easiest parts of this project including the brainstorming and ideation as well as the actual wiring of the parts. We felt there was only a limited number of options for our middle step since it would be difficult to physically modulate anything in our middle step to use something like distance/distance sensor. However, upon seeing what other groups have completed, we realized we could have been more creative in this project by using outputs and motors we do have to create a physical middle step, perhaps something similar to our wind sensor idea but more practical for this class. However, using color as a middle step did invoke some challenges as well that we didn’t think we would run into.
Though the soldering overall was easy, Nish did learn something that should have been intuitive. Although RGB LEDs individually have different current values they need in order to not blow up, if you use different resistors on each one, the intensity of each color will show up differently. She had already soldered in her resistors when she realized this, and this mistake was reflected when the color sensor was not able to sense as much red as should have been outputted in the LED.
Learning how to use the color sensor was an interesting challenge, though not a difficult one. Since we were using a sensor that was not a part of our kit, there wasn’t necessarily a module that described how to wire it and code it, so our team had to use our own research to find our model and other projects that had used the same sensor. Since this is a common sensor it was not that difficult to find Arduino code online that we could model ours after. While we probably don’t understand why it’s coded in the way it is and didn’t fully utilize all the functions, we were able to successfully sense the output of our RGB LED.
The most difficult part of our project was figuring out how to go from three inputs (X, Y, Z orientation) to just one final output, where different combinations of the three inputs would lead to a meaningful differences in the final output. Several different input positions can lead to the same output, as we discovered that the X, Y, Z inputs tend to vary around the same values for the most part (between 200 and 300, though their individual ranges can go between 0 and 500). Therefore, if we mapped the entire 0 to 500 value to a 0-255 range for our RGB values, we would end up with very similar colors no matter what the orientation is, so we chose to map our X, Y, and Z values of only 200-300 to a 0-255 range for later use. However, for some reason when we tried to map to a 0-99 range for our output into the LCD, the Arduino program did not like that and we had to change our range of the X, Y, Z inputs from 200-300 to 0-500, resulting in the LCD representation for our input having a very small range that was not consistent with the rest of the numbers that had been calculated from a 200-300 range mapping. We are still unsure as to why the Arduino program did not let us map from a 200-300 range in this portion, and hope to eventually get more consistent numbers between our four outputs on the LCD.
We also ran into a problem where despite using code similar to our previous outputs to the LCD, the representation of the vibration output was coming out negative. Both of us individually solved this in different ways, with one of us using “constrain” from 0 to 99, and the other changing this output from an int to an unsigned int. While we learned different methods to solve the problem, we are still unsure of the root cause of why we got a negative output in the first place, since I don’t believe we caused any overflow and we were only working with positive numbers that had worked fine up until that calculation. Throughout these several issues with mapping, we were able to pick up handy debugging skills, such as using the Serial.print() function for both strings and variables in intermediate steps to see what our numbers were before we mapped them, and to find the appropriate typical range for each of our sensors.
We probably could have used more features of the color sensor if we had explored more. Since we were going from three inputs to one output, and our X, Y, Z ideally directly maps to an RGB value, respectively, we perhaps could have utilized the “lux” sensor. Our color sensor was able to sense not only red, green, and blue values, but also the intensity. Looking back, this may have made more sense to use it’s a more direct way of going from the color itself to a single property of the color, rather than trying to arithmetically calculate a single output value for vibration from three values of red, green, and blue. It also would have made our middle step more significant in calculating the final output, rather than just something we had to put in for this project since we definitely could have used a similar formula to directly calculate vibration from the X, Y, Z coordinates.
The Functional Block Diagram for our project shows a high level of the inputs and outputs.
The Schematic of our project presents a more detailed view of how the components are connected to each other.
#include <Wire.h> #include "Adafruit_TCS34725.h" // for color sensor #include <LiquidCrystal.h> // for LCD /* Daniela Castleburg & Nish Nilakantan Double Transducer: Orientation to Color to Vibration This code takes in X, Y, Z coordinates of an input sensor and maps them to RGB values which create a color outputted by the RGB LED, which is then sensed by the color sensor and mapped to a single value that represents the strength of output vibration. Pin Table: pin | mode | description -------------------------------------- A0 input x position A1 input y position A2 input z position A4 input color sensor (SDA) A5 input color sensor (SCL) 3 output pancake vibrator 4 output d4 LCD 5 output d5 LCD 6 output d6 LCD 7 output d7 LCD 9 output Blue LED 10 output Green LED 11 output Red LED 12 output EN LCD 13 output RS LCD Credits for Color Sensor code: https://learn.adafruit.com/adafruit-color-sensors/arduino-code */ // INPUT // // Accelerometer pins const int accelXPin = A0; // Analog input pin for X movement const int accelYPin = A1; // Analog input pin for Y movement const int accelZPin = A2; // Analog input pin for Z movement // Variables to keep track of the current positions int accelXPos = 0; int accelYPos = 0; int accelZPos = 0; // MIDDLE STEP // // RGB LED pins const int RED = 11; const int GREEN = 10; const int BLUE = 9; /* Initialise with specific int time and gain values */ Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_700MS, TCS34725_GAIN_1X); // OUTPUT // const int PANCAKE = 3; // initialize the library by associating any needed LCD interface pin // with the arduino pin number it is connected to const int rs = 12, en = 13, d4 = 4, d5 = 5, d6 = 6, d7 = 7; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); // Other variables const int WAIT = 200; unsigned long lastTime = 0; // MQTT int inVal, outVal; void setup() { // begin serial communication Serial.begin(115200); Serial.setTimeout(50); // finding the color sensor if (tcs.begin()) { Serial.println("Found sensor"); } else { Serial.println("No TCS34725 found ... check your connections"); while (1); } // RGB LED as output pinMode(RED, OUTPUT); pinMode(GREEN, OUTPUT); pinMode(BLUE, OUTPUT); // Setup the accelerometer inputs pinMode(accelXPin, INPUT); pinMode(accelYPin, INPUT); pinMode(accelZPin, INPUT); // Pancake Vibration Motor as output pinMode(PANCAKE, OUTPUT); // set up the LCD's number of columns and rows: lcd.begin(16, 2); } void loop() { uint16_t r, g, b, c, colorTemp, lux; // Get the current states of accelerometer accelXPos = analogRead(accelXPin); accelYPos = analogRead(accelYPin); accelZPos = analogRead(accelZPin); int accelAbs = sqrt(accelXPos ^ 2 + accelYPos ^ 2 + accelZPos ^ 2); // absolute acceleration for LCD // map acceleration input to LED output int redVal = map(analogRead(accelXPin), 200, 300, 0, 255); int greenVal = map(analogRead(accelYPin), 200, 300, 0, 255); int blueVal = map(analogRead(accelZPin), 200, 300, 0, 255); // Use mqtt to get input data // int redVal = map(inVal,0,99,0,255); // int greenVal = map(inVal,0,99,0,150); // int blueVal = map(inVal,0,99,0,100); int LEDAbs = sqrt(redVal ^ 2 + greenVal ^ 2 + blueVal ^ 2); // combination of LED values for LCD // update LED color based on accelerometer position analogWrite(RED, redVal); analogWrite(GREEN, greenVal); analogWrite(BLUE, blueVal); // data from color sensor tcs.getRawData(&r, &g, &b, &c); int colorSensorAbs = sqrt(r ^ 2 + g ^ 2 + b ^ 2); // combination of color sensor values for LCD // map color sensor data to pancake input int pancakeVal = map(colorSensorAbs, 100, 250, 0, 255); // update pancake vibration motor based on color sensor analogWrite(PANCAKE, pancakeVal); // get pancake value //int normOutput = analogRead(PANCAKE); // normalize all variables to 0-->99 range int normInput = map(accelAbs, 0, 500, 0, 99); // Accelerometer int normMiddleOutput = map(LEDAbs, 0, 255, 0, 99); // RGB LED int normMiddleInput = map(colorSensorAbs, 0, 500, 0, 99); // Color Sensor int normOutput = map(pancakeVal,-350,350,0,99); // Pancake Vibration motor // print information to LCD if (millis() - lastTime > WAIT) { lcd.setCursor(0, 0); lcd.print("i:"); lcd.setCursor(2, 0); lcd.print(normInput); lcd.setCursor(6, 0); lcd.print("m:"); lcd.setCursor(8, 0); lcd.print(normMiddleOutput); lcd.setCursor(8, 1); lcd.print(normMiddleInput); lcd.setCursor(12, 1); lcd.print("o:"); lcd.setCursor(14,1); lcd.print(normOutput); outVal = normOutput; transmitSerialSignal(); lastTime = millis(); // update timer variable } } // Mqtt functions void serialEvent(){ while(Serial.available() > 0){ inVal = Serial.parseInt(); } } void transmitSerialSignal(){ Serial.println(outVal); }
]]>
Distance to pressure double transducer.
Distance to Pressure Double Transducer – Daniel Zhu
Close up of the method servo used to apply pressure to the paper
Close up of servo in position to press the lever switch intermittently when its shaft is turned programmatically
As the distance between the Ultrasonic Distance Sensor and an object decreases, the rate at which a servo motor presses a lever switch increases. As the lever switch registers a faster and faster rate of being triggered, a second servo motor presses a stack of cardboard more closely to the table, applying more pressure. Conversely, as the distance between the Ultrasonic Distance Sensor and an object increases, the rate at which a servo motor presses a lever switch decreases. As the lever switch registers slower rate of being triggered, a second servo motor presses a stack of cardboard less closely to the table, relieving pressure.
Noise in Ultrasonic Distance sensor data.
Converting rotational motion into pressure
Unsoldered prototype
This project introduced numerous physical, coding, and conceptual difficulties. Conceptually, it was difficult for us to come up with an idea to convert distance into pressure: while we had spent a lot of time working with light as a medium (LEDs) and movement, we hadn’t ever worked with any electrical components that had a specific function of exerting pressure. After speaking with the professor, we learned that in order to exert pressure, we could take inspiration from a crank that used a circular turning motion to push and pull an object. We then decided upon the idea of our second transducer outputting an angle change for a motor so it could turn like a crank and push and pull an object we created. The object in turn would apply pressure to a piece of paper as it was being pushed.
Physically, our challenge lay in orienting the different components of the machine (i.e. the servos and the button) so that they would form a chain reaction (i.e. a servo pressing a button intermittently, causing the other servo to intermittently apply pressure to a piece of paper). It took some trial and error to figure out which button was most responsive to the servo’s touch, as well as whether a servo or stepper motor was better suited to our task. It became too technically difficult for us to implement the crank with the stepper motor providing the circular turning motion, like we had originally intended, so we ended up going with the servo. In the future, we would want to be able to utilize the stepper motor as it would allow for more creativity in our output, since it turns 360 degrees.
Coding-wise, our challenge lay in figuring out the relationship between the first servo, the button, and the second servo, and how to pass information through each of these components. Specifically, we had to figure out how to convert the digital intermittent pressing of the button into an analog angle for the second servo. This also took a lot of debugging and trial and error, as we didn’t immediately know which angles of the servo were most optimal for pressing on the paper, and we didn’t know how we could change the angles such that the servo would be intermittently pressing down and intermittently going back up based on the button-pressing speed of the first servo.
Even though this project was extremely difficult, since we were relative Arduino beginners, we were able to go far out of our comfort zone and learn to put together various moving parts, as well as how to convert an analog signal to a digital signal and back to an analog signal.
Block Diagram
Schematic Diagram
/* 60-223, Project 1: Double Transducer Daniel Zhu (dszhu) time spent: 6+ hrs Collaboration and sources: 1) Code for LCD taken from the course website. 2) Code for Servo Motor take from course website. Challenge(s): A faulty barrel jack charger as well as noise in the LCD Display due to it being connected to a power source shared by servo motors caused it to display egyption hieroglyphics for several hours. Description: As the distance between the Ultrasonic Distance Sensor and an object decreases, the rate at which a servo motor presses a lever switch increases. As the lever switch registers a faster and faster rate of being triggered, a second servo motor presses a stack of cardboard more closely to the table, applying more pressure. Pin mapping: Arduino pin | type | description ------------|--------|------------- 2 output LCD Display Coding Pin 3 output LCD Display Coding Pin 4 output LCD Display Coding Pin 5 output LCD Display Coding Pin 6 output LCD Display E 7 input LCD Display RS 8 output Output Servo Motor (Pressure) 9 output Middle Servo Motor (Morse) 10 input Lever Switch Input 11 output TRIGGER_PIN - Ultrasonic Distance Sensor */ #include <Servo.h> //Servo Library #include <NewPing.h> //Ultrasonic Distance Finder Library #include <LiquidCrystal.h> //LCD Library #include <Wire.h> //LCD Library //Ultrasonic Distance Sensor Pins const int TRIGGER_PIN = 12; // Arduino pin tied to trigger pin on the ultrasonic sensor. const int ECHO_PIN = 11; // Arduino pin tied to echo pin on the ultrasonic sensor. const int MAX_DISTANCE = 200; // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm. NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); //Createing a new Sonar object for use. const int SDELAY = 50; // Minimum amount of delay between sonar pings long sTimeSince = 0; // Time since last sonar ping int dist = sonar.ping_cm(); // Keeps track of object distance from sensor //Servo Motor Pins const int BTN = 10; // Lever Switch const int SERVO_IN = 9; // Middle Servo const int SERVO_OUT = 8; // Output Servo Servo Morse; Servo Pressure; long mTimeSince = 0; // Time since Middle Servo Triggered bool mSwitch = false; // Keeps track of whether the servo is in the motion of pressing or returning long pTimeSince = 0; // Time since Output Servo Triggered int prsRate = 0; // Lever Switch input press rate //LCD Display const int rs = 7, en = 6, d4 = 2, d5 = 3, d6 = 4, d7 = 5; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); long lTimeSince = 0; //Time Since LCD Display was last updated const int lcdDelay = 250; // Minimum time between LCD updates void setup() { pinMode(BTN, INPUT); Morse.attach(SERVO_IN); Pressure.attach(SERVO_OUT); Serial.begin(115200); lcd.begin(16, 2); } void loop() { if (millis() - sTimeSince > SDELAY) { // Triggering the Ultrasonic Sensor + Finding the Distance dist = sonar.ping_cm(); sTimeSince = millis(); } int pause = map(dist, 0, 50, 500, 1000); // Converts distance into how long the middle servo pauses between presses if (int(millis() - mTimeSince) > pause) { //pressing the middle servo motor if (mSwitch == false) { // Pressing the Lever Switch Morse.write(40); mSwitch = true; } else { // Unpressing the Lever Switch Morse.write(45); mSwitch = false; } prsRate -= 10; // Automatically Subtracting from the Pressrate of the Lever Switch Serial.print("Delay: "); Serial.println(pause); mTimeSince = millis(); } if (digitalRead(BTN) == 1) { // Detecting LEvel Switch Input prsRate += 20; } if (prsRate > 100) { // Capping the pressrate before its conversion to angle to prevent servo failure prsRate = 100; } if (prsRate < 0) { prsRate = 0; } int angle = map(prsRate, 0, 100, 30, 20); // Converting the Pressrate of the Lever Switch to an Angle if (millis() - pTimeSince > 500) { Pressure.write(angle); // Moving the output motor to that angle } if (int(millis() - lTimeSince) > lcdDelay) { // LCD Display delay // Converting Middle values into 0-99 range for LCD int input = map(dist, 0, 50, 0, 99); int middleA = map(pause, 500, 1000, 0, 99); int middleS = map(prsRate, 0, 100, 0, 99); int output = map(angle, 20, 30, 0, 99); lcd.setCursor(0, 0); lcd.print((String)"i:" + input + " m:" + middleA); lcd.setCursor(8, 2); lcd.print((String)middleS + " o:" + output); lTimeSince = millis(); } }
]]>
Tinkercad does not have a lot of components that are present in the real life version, so there are some fake data simulation. In real life, our first sensor is an accelerometer, but we simulated it with a potentiometer. The values are mapped to be in between 0-100 to mimic values we could get from our accelerometer. From there, the data is mapped to a value between 130-180 so serve as an angle for the servo motor. In real life, the servo motor would move an object in front of an ultrasonic ranger, but instead, we have to fake the simulation. The distance output from the ultrasonic ranger is then mapped to a value between 100 and 600 to serve as tone for the buzzer.
Photos for Scale:
Shuyu:
Vishnu:
Detail Photos
Initially, getting data from the accelerometer was a little foreign, so it took some thinking on how we would utilize the data.
The accelerometer was a new tool to us, so we had to research on how to wire and write code for it.
Testing everything with a pancake motor at the end.
Building the arm that extend back and forth had its mechanical difficulties. It took a while to troubleshoot how far the arm could extend.
Videos:
Shuyu:
Vishnu:
Narrative Description:
Our double transducer takes in vibration as an input (via a 3-axis accelerometer), and converts that input to an angle for a servo motor. The servo motor is attached to a popsicle stick “arm” that extends based on the angle. An ultrasonic ranger detects how far the popsicle sticks are from it and the resulting distance (cm) is converted into a tone for an 8-ohm speaker.
Progress Photos:
Shuyu: This was when I first successfully wired and ran some code through the LCD display. It was super exciting because the wiring was a little intense and something could have easily gone wrong.
Shuyu: Using a transistor to wire a speaker was a new concept and wiring it correctly was a major step in the overall project.
Vishnu: Troubleshooting the popsicle arm and trying to find a good angle range. I ended up building barriers for it.
Vishnu: Wiring the speaker had some challenges because wires were not connected into the right ports.
Discussion:
The entire process of brainstorming, planning, and creating the double transducer was filled with its unique set of problems and triumphs. From the beginning, we were not familiar with vibration as an input and we were not sure how ambitious we should be with our middle step. We played around with a couple of ideas, including a basic vibration to light to sound workflow. Excluding the vibration portion, we were confident we could get the “light” middle step (LED + photoresistor) to work and output sound; this would serve as a good backup plan in case a more ambitious setup failed.
The ambitious idea we settled on was a system of vibration to movement to sound. We decided to split the “movement” into two parts: One part performing the movement (a Servo motor with a lever attached), and the other part sensing proximity and outputting a distance value (via an ultrasonic ranger). We found that while the idea was simple enough, the middle step ran into significant challenges.
Most notably, we had to restrict the servo motor from its total range, in order to constrain it to the board as well as prevent it from ripping out of its supports. This was mainly achieved through trial and error, and each of us had slightly different ranges based on the physical layout of our board and device.
With help from Zach, we realized that a good stand-in for a vibration sensor was a 3-axis accelerometer. The accelerometer senses the position (X,Y, Z) of the device if it is even slightly shifted. We realized that this could work to our advantage, although we needed to manually manipulate it to achieve the output. We found that because we had to twist the accelerometer around, we had to make sure to bolt the apparatus down through soldering and tape.
This project definitely pushed us to understand the real-world limitations of theoretical concepts. While there were chunks of the project we could neatly resolve in software, there were other parts we simply had to stress-test through empirical trials (such as the servo extension). Overall, we came away with a solid understanding of the relationship between different mediums, as well as the interlocking nature of software and hardware in electronics.
Functional Block Diagram:
Schematic:
Code:
/* 60-223, Double Transducer : Vibration to Movement to Sound Shuyu Zhang, Vishnu Raghavan Description: Our doubel transducer takes in vibration as an input. The input is measured by a 3-axis accelerometer. The difference between the input from the x-axis and y-axis is then mapped to an angle for a servo motor (130-180 degrees). In real life, the servo motor is attached to a popsicle arm that will extend based on the angle the servo motor moves to. An ultrasonic ranger sits in front of the popsicle arm and measures how close or far the arm is from it. The data from the ultrasonic ranger is then mapped to a tone (100-600) which is the output for a speaker. Sources: 1) Heavily referenced the program on the maker cards website when programming the accelerometer 2) Incorporated some of the code of an example from the HCSR04 library in order to read distance with the ultrasonic ranger 3) Referenced the Servo Motor example from class to program the servo motor 4) Used LCD Display wiring and program from Arudino Website to program the LCD Display Pin mapping: Arduino pin | type | description ------------|--------|------------- 12 LCD Pin Used to initialize the LiquidCrystal Library 11 LCD Pin Used to initialize the LiquidCrystal Library 5 LCD Pin Used to initialize the LiquidCrystal Library 4 LCD Pin Used to initialize the LiquidCrystal Library 3 LCD Pin Used to initialize the LiquidCrystal Library 2 LCD Pin Used to initialize the LiquidCrystal Library 9 Output Servo Motor pin 6 Output Buzzer pin 13 Input Trigger Pin in the Ultrasonic ranger 7 Input Echo Pin in the Unltrasonic ranger A0 Input Analog input pin for X movement A1 Input . Analog input pin for Y movement A2 Input Analog input pin for Z movement */ #include <HCSR04.h> #include <Servo.h> #include <LiquidCrystal.h> const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); Servo motor; byte triggerPin = 13; byte echoPin = 7; const int SERVOPIN = 9; //Analog const int BUZZER = 6; //Analog output in for 8Ω Speaker const int ACCELXPIN = A0; // Analog input pin for X movement const int ACCELYPIN = A1; // Analog input pin for Y movement const int ACCELZPIN = A2; // Analog input pin for Z movement const int TIMERWAIT = 1000; const int MOTORWAIT = 200; // Variables to keep track of the current positions int accelXPos = 0; int accelYPos = 0; int accelZPos = 0; int dist; int angle; int currTone; int accelData; unsigned long timer = 0; unsigned long motorTimer = 0; void setup() { Serial.begin(9600); //Set up for ultrasonic ranger HCSR04.begin(triggerPin, echoPin); //Set up for servo motor motor.attach(SERVOPIN); // Setup the accelerometer inputs pinMode(ACCELXPIN, INPUT); pinMode(ACCELYPIN, INPUT); pinMode(ACCELZPIN, INPUT); //Setup for buzzer output pinMode(BUZZER, OUTPUT); // set up the LCD's number of columns and rows: lcd.begin(16, 2); } void loop() { // Get the current states accelXPos = analogRead(ACCELXPIN); accelYPos = analogRead(ACCELYPIN); accelZPos = analogRead(ACCELZPIN); //declares a variable to read distance for ultrasonic ranger double* distances = HCSR04.measureDistanceCm(); accelData = abs(accelXPos - accelYPos); //Gives range of (0-90) //Mapping Accelerometer data to an angle angle = map(accelData, 0, 90, 130, 180); //Set at a delay of 200 milliseconds //c if (millis() - motorTimer > MOTORWAIT) { motorTimer = millis(); dist = (int)(distances[0]); currTone = map(dist, 0, 30, 100, 600); tone(BUZZER, currTone); //changes angle based on accelerometer input //changes tone based on distance motor.write(angle); delay(200); tone(BUZZER, currTone); motor.write(angle); delay(200); } //LCD Display Code if (millis() - timer > TIMERWAIT) { timer = millis(); lcd.setCursor(8, 1); int midSen = map(dist, 0, 30, 0, 99); lcd.print(midSen); if (midSen > 100) { lcd.clear(); } //First Row on LCD lcd.setCursor(0, 0); lcd.print("i:"); int input = map(accelData, 0, 90, 0, 99); lcd.print(input); lcd.setCursor(7, 0); lcd.print("m:"); int midAct = map(angle, 130, 180, 0, 99); lcd.print(midAct); //Second Row on LCD lcd.setCursor(12, 1); lcd.print("o:"); int output = map(currTone, 200, 600, 0, 99); lcd.print(output); } }
]]>
(Jud)
(Jun)
The cardboard stand on the left of the transducer has a pressure sensor attached to it which senses how hard it is pressed. Based off of this pressure, a light inside of the cardboard box turns on to a specific brightness. If the pressure sensor is pushed hard, then the light turns on very bright, and if it is pressed lightly, then the light is very dim. Inside of this box is also a light sensor that senses this change in the clear light’s brightness. According to the amount of change that occurs, a servo on the left side of the mechanism turns around. If the amount of light in the box increases, then the servo rotates counter-clockwise to match this amount of change. As all of this is happening, a display at the bottom of the machine shows the numeric values of all of these actions starting with the pressure sensor in the top left and ending with the servo in the bottom right of the screen.
This image shows the soldering of the color sensor that we were originally going to use. The soldering was successfully done, and the device was working, but we decided not to use this sensor.
This image depicts the testing of the color output using three different color LED’s to see what was causing our multi-colored LED issue discussed later on.
This image shows the final step of the soldering process where our switch from color sensing to brightness became permanent.
This image shows the process of adding the potentiometer to the LCD in order to adjust its brightness. Initially, the v0 was connected to the ground instead of the potentiometer, and we were not able to adjust its brightness.
The hardest part about this project was understanding how to use each of the components and figure out accurate mapping functions to make sure each component was being used to its full potential. The hardest component accomplish this with was the color sensor because of its 3 inputs. Since the color sensor has red, green, and blue inputs, the Force Sensitive Resistor’s (FSR) single output made connecting these two elements very challenging. In the end, after relentlessly searching for algorithms that would accomplish this goal for us, we decided to pursue an alternative method utilizing brightness instead. Reflecting back on this decision, however, it would’ve been a better idea to start out with trying to understand this conversion process and write our own algorithm as opposed to searching the internet for one we could implement. This could have possibly improved our ability to understand these converting algorithms later on if we needed to use them which may have resulted in the final product including this middle sensor.
Apart from this color sensing dilemma, we also ran into issues while trying to use the servo library and analogWrite functionality together. During our first testing stages of the different components, we had the multi-colored LED wired in such a way that it would receive PWM signals from pins 6, 9, and 10. Upon plugging everything in and trying to change the color of the LED, only the LED plugged into pin 6 would produce the desired effect. After lots of time spent trying to debug the circuit, we finally figured out that the issue existed with the way that the servo library interacts with the Arduino. In order for the servo library to function, part of the code disables the analogWrite function for pins 9 and 10 making our original circuit diagram invalid. Just switching the input pin placement for these LED’s fixed our problem entirely.
Reflecting back on the creativity of the project, it would’ve been both easier and more exciting/interesting to only use the red and blue LED’s for the middle step of the transducer. This would not only make the code simpler by removing the need to map one input to three values and back, but it would also have a more physical meaning aligning closer to traditional ways of depicting pressure. Adding this element could have made the transducer a little bit more interactive helping to produce a more meaningful interaction than what this final version creates.
/* 60-223, Double Transducer Judson Kyle (judsonk) Hyojun Jeong (hyojunj) Collaboration and sources: 1) Used sample from Code Bites page on the 60-223 Intro to Physical Computing website for serial communication to the MQTT bridge application Description: This double transducer will convert pressure to light to orientation through the use of a Force Sensitive Resistor (FSR), a photoresistor, and a servo. The input of the FSR will drive the brightness of an LED which is in an enclosed box with the photoresistor. The photoresistor will read the LED brghtness and map it to an angle which the servo can travel to. This will continuously happen until the system is turned off. In addition, the input and output values for each sensor and output will be mapped to a value from 0-99 and then displayed in an LCD screen with the FSR reading on the top left, the LED birghtness in the middle top, the photoresistor reading in the bottom middle, and the servo output in the bottom right. Pin mapping: Arduino pin | type | description ------------|--------|------------- A0 input Force Sensitive Resistor for sensing pressure A1 input Photoresistor for sensing birhgtness ofLED 3 output LED pin for controlling brightness 4 output LCD enable pin 7 output LCD register select pin 8 output LCD pin D4 9 output LCD pin D3 10 output LCD pin D6 11 output Servo pin for control of servo's angle 12 output LCD pin D7 */ //Include Libraries for LCD and Servo Motor #include <LiquidCrystal.h> #include <Servo.h> //FSR pins const int FSRPIN = A0; //Photoresistor and LED pins const int PHOTOPIN = A1, LEDPIN = 3; //Servo object and variable creation Servo gauge; //Create servo object "gauge" int pos = 0; //Set pos varible to 0 (stores position of servo) const int SERVOPIN = 5; //Variable holding servo pin number //LCD object creation and pin attachment const int en = 4, rs = 7, d7 = 12, d6 = 10, d5 = 9, d4 = 8; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); //Prepare timing values to reduce delay of writing to LCD const int waitingValue = 100; // Time delay between readings unsigned long lastTime = 0; // Variable to store the most recent time //MQTT Variables const unsigned long WAIT = 250; //25sec unsigned long timer; int inVal, outVal; //Code to run once void setup() { //LCD Initialization lcd.begin(16, 2); lcd.print("Hello"); //Define inputs pinMode(FSRPIN, INPUT); pinMode(PHOTOPIN, INPUT); //Define outputs pinMode(LEDPIN, OUTPUT); //Set gauge object to read from SERVOPIN gauge.attach(SERVOPIN); Serial.begin(115200); // qt_arduino_mqtt_bridge uses this rate Serial.setTimeout(50); // wait ≤ 50 milliseconds to parse incoming data } //Code to loop through void loop() { // //Recieve input from previous transducer // int ledVal = map(inVal, 0, 99, 0, 255); //Convert pressure into light int fsrValue = analogRead(FSRPIN); //Read FSR value and sotre in fsrValue int ledVal = map(fsrValue, 0, 1000, 0, 255); //convert pressure to PWM signal analogWrite(LEDPIN, ledVal); //Display brightness //Convert light into servo orientation int photoVal = analogRead(PHOTOPIN); //Read photoresistor to get brightness int servoAngle = map(photoVal, 0, 750, 0, 180); //Convert brightss to degreeof rotation //Rotate servo to correct angle and wait for it to get there gauge.write(servoAngle); delay(15); //Output to LCD after every time interval if (millis() - lastTime > waitingValue) { lcd.clear(); lcd.print("i:" + String(map(fsrValue, 0, 1000, 0, 99))); //Print FSR input value in 0-99 range //Set location of next print line to middle of the first row lcd.setCursor(6, 0); lcd.print("m:" + String(map(ledVal, 0, 255, 0, 99))); // Print LED value in 0-99 range //Set location of next print to be the bottom middle of the LCD lcd.setCursor(6, 1); lcd.print(String(map(photoVal, 0, 750, 0, 99))); //color sensor output value in 0-99 range //Set location of next print to be the bottom right of the LCD lcd.setCursor(11, 1); lcd.print("o:" + String(map(servoAngle, 0, 170, 0, 99))); // Print servo output value in 0-99 range lastTime = millis(); } //Transmit output data to next device if (millis() - timer > WAIT){ outVal = map(servoAngle, 0, 170, 0, 99); transmitSerialSignal(); timer = millis(); } } void serialEvent(){ /* You do *not* need to call this function in your loop; it is a special function that will run whenever serial data flows into the Arduino. */ /* The function assumes your machine's input value is called "inVal" (change that variable name as needed) */ // if there is incoming serial data, read it while (Serial.available() > 0){ // interpret incoming digits as integer and save into inVal inVal = Serial.read(); } } void transmitSerialSignal(){ /* You should call this function 2-4 times/second in your main loop to transmit information to the next Arduino in the transmission chain. */ /* Assumes your machine's outgoing value is called outVal (change that variable name as needed) */ Serial.println(outVal); }
]]>
Tinkercad:
* the potentiometer on the left is representative of the rotary encoder. The DC motor and driver are representative of the stepper motor and driver
Overall:
Nicole’s final double transducer
Kevin’s Final Double Transducer
Detail Photos:
LCD input/output values
Servo motor “dial” (from 0º – 180º)
Close up of the connection between the stepper motor and rotary encoder
Final protoboard wiring
Brief Movies:
Description:
This device detects the amount of light through a photoresistor, and sends that signal to the stepper motor driver and stepper motor, which turns the rotary encoder at a certain speed. The rotary encoder determines how far it was rotated, and sends that data to the servo motor, which based on the data, turns to a certain angle (orientation).
Progress Images:
Figuring out the connection between the stepper motor and rotary encoder [Nicole]
An LCD that needs to be debugged [Nicole]
LCD setup with “Hello World” starter code from library [Kevin]
In-progress picture of soldering the photoresistor and encoder circuits onto a protoboard [Kevin]
Testing all the individual components put together for the first time! [Kevin]
Discussion:
I think one of the biggest hurdles for us was dealing with the unknown. We chose to use a rotary encoder, something that we weren’t really familiar with, causing a lot of uncertainty. We also had no idea how we were going to connect the stepper motor and rotary encoder together, so we winged that as well. In hindsight, I think we should’ve fleshed out our plan to the smallest detail before starting rather than going in with a vague idea. That would’ve saved a lot of headaches. However, on the other hand, going in with vague idea was a great way to force one to solidify their idea, since planning can only do so much.
Nicole: Planning first would’ve also helped me with my wiring, since once the LCD Display came along with its 6 more pins, I was just sticking them wherever there was available space. Ideally, if I had planned it out, I would have the pins consecutive with each other.
One of our challenges was that we needed 12 digital pins, meaning that we were either forced to do some challenging wiring to use something like a shift register or sacrifice one of the TX/RX pins. At first neither of us knew exactly what the implications of that would be, but we found out that we could not upload code to the Arduino or use serial communication while using these pins. This ended up being a headache when we were debugging issues with our code and wiring as if we forgot to unplug the TX pin the code failed to upload. The wire ended up breaking inside the pin, although luckily some needle nose pliers saved the day. I will be trying my best to avoid using these pins in the future by using a shift register or investing in an Arduino board with more pins.
Diagrams:
Functional Block Diagram
Functional Block Diagram
Schematic Diagrams
Nicole’s electrical schematic
*a few of Kevin’s pins are slightly different (see code), but the wiring is identical otherwise
Code:
Nicole’s Code
/* 60-223, Project 1: Double Transducer Nicole Yu (nvy) Kevin Bender (kbender1) Description: This device detects the amount of light through a photoresistor, and sends that signal to the stepper motor driver and stepper motor, which turns the rotary encoder at a certain speed. The rotary encoder determines how far it was rotated in the span of 2 seconds, and sends that data to the servo motor, which based on the data, turns to a certain angle (orientation). Pin mapping: Arduino pin | type | description ------------|--------|------------- A0 input photoresistor 2 input rotary encoder pin A 3 input rotary encoder pin B 5 output servo motor 7 input stepper motor IN1 8 input stepper motor IN2 9 input stepper motor IN3 10 input stepper motor IN4 A1 output LCD RS 4 output LCD Enable 6 output LCD D4 11 output LCD D5 12 output LCD D6 13 output LCD D7 Sources: 1) How to wire and program the LCD from the Elgoo website: https://drive.google.com/drive/folders/1BUe40Ibb9Bzi1JwRKywO1PCXBMzkFXm7?usp=sharing 2) I borrowed heavily from this Stepper motor library and code: https://www.arduino.cc/en/Tutorial/LibraryExamples/StepperSpeedControl 3) I followed this tutorial and borrowed heavily its code: https://www.seeedstudio.com/blog/2020/01/19/rotary-encoders-how-it-works-how-to-use-with-arduino/ 4) I referenced the Canvas modules to refresh on how to wire and code for a photoresistor and servo */ // LIBRARIES #include <Encoder.h> #include <Stepper.h> #include <Servo.h> #include <LiquidCrystal.h> // VARIABLES const int PHOTOPIN = A0; // photoresistor pin const int DOORMOTORPIN = 5; // servo motor pin const int stepsPerRevolution = 200; // stepper motor's const int WAIT = 2000; // 2 seconds unsigned long timerVariable = 0; int orientation; long oldPosition = -999; // initialize library code LiquidCrystal lcd(A1, 4, 6, 11, 12, 13); Stepper myStepper(stepsPerRevolution, 7, 8, 9, 10); Encoder myEnc(2, 3); Servo doorMotor; void setup() { // set up the LCD's number of columns and rows: lcd.begin(16, 2); // servo doorMotor.attach(DOORMOTORPIN); } void loop() { // PHOTORESISTOR int photoVal = analogRead(PHOTOPIN); // STEPPER MOTOR int motorSpeed = map(photoVal, 0, 1023, 0, 100); if (motorSpeed > 0) { myStepper.setSpeed(motorSpeed); myStepper.step(stepsPerRevolution / 100); // ROTARY ENCODER long newPosition = myEnc.read(); // SERVO int orientation = map(newPosition, 0, 17, 10, 170); // the 17 is a placeholder, // I haven't seen the rotary encoder go past 16 // Sees at 2 seconds how far rotary encoder turned if (millis() - timerVariable > WAIT) { doorMotor.write(orientation); myEnc.write(0); // resets encoder to 0 // LCD // update LCD here lcd.setCursor(0, 0); lcd.print("i:"); // input lcd.print(photoVal); lcd.print(" "); // solution to non erasing problem lcd.setCursor(8, 0); lcd.print("m1:"); // middle step 1 lcd.print(motorSpeed); lcd.print(" "); lcd.setCursor(0, 1); lcd.print("m2:"); // middle step 2 lcd.print(newPosition); lcd.print(" "); lcd.setCursor(8, 1); lcd.print("o:"); // output lcd.print(orientation); lcd.print(" "); timerVariable = millis(); } // not > 2 seconds, update the old position else { if (newPosition != oldPosition) { oldPosition = newPosition; } } } }
/* * 60-223, Project 1 * Kevin Bender (kbender1) * time spent: ~8 hours * * Collaboration and sources: * 1) Nicole Yu and I worked together on the idea and * electrical design, coded separately (other than tinkercad) * 2) I borrowed from the Elegoo starter kit lesson 2.3 for the * LCD * 3) I borrowed from the Encoder library examples * 6) I borrowed from the stepper library examples * * Challenge(s): This project took a lot of learning and research * as it combined everything we had learned so far about * Arduino and electronics. The challenges came in the form * of trying to figure out how to use the LCD which didn't work * with the manufacturer-recommended wiring of 3.3v and with the * stepper motor which was much weaker than expected. * * Next time: I will try to learn more about potential inputs/outputs * and possibly try them out in advance. A lot of suffering could * have been saved by doing this with the stepper motor * * Description: Object detects light level, based on how intense light is it would cause a motor to spin fast/slow, * and that speed would then affect the orientation of the servo motor “dial” (from encoder) * * Pin mapping: * * Arduino pin | type | description * ------------|--------|------------- * A0 input Photoresistor as initial input * 1 input rotary encoder pin A for middle step input * 13 input rotary encoder pin B * 2 output Stepper motor driver pin A as middle step output * 3 output Stepper motor driver pin B * 4 output Stepper motor driver pin C * 5 output Stepper motor driver pin D * 6 output servo motor for final output * 7 input pushbutton to activate lights * 8 output LCD pin A * 9 output LCD pin B * 10 output LCD pin C * 11 output LCD pin D * 12 output LCD pin E */ // include the library code: #include <LiquidCrystal.h> #include <Encoder.h> #include <Stepper.h> #include <Servo.h> const int stepsPerRevolution = 200; const int ENC1 = 1; const int ENC2 = 13; const int LCD1 = 7; const int LCD2 = 8; const int LCD3 = 9; const int LCD4 = 10; const int LCD5 = 11; const int LCD6 = 12; const int STEP1 = 2; const int STEP2 = 3; const int STEP3 = 4; const int STEP4 = 5; const int SERVOPIN = 6; const int PHOTORESISTOR = A0; // initialize the library with the numbers of the interface pins LiquidCrystal lcd(LCD1, LCD2, LCD3, LCD4, LCD5, LCD6); Encoder myEnc(ENC1, ENC2); Stepper myStepper(stepsPerRevolution, STEP1, STEP2, STEP3, STEP4); Servo myservo; void setup() { myservo.attach(SERVOPIN); // put your setup code here, to run once: lcd.begin(16, 2); // Print a message to the LCD. pinMode(PHOTORESISTOR, INPUT); } void loop() { long t0 = millis(); long speed = 0; long lastPos = myEnc.read(); while (true) { long t = millis(); myEnc.read(); int motorSpeed = map(analogRead(PHOTORESISTOR), 0, 1023, 0, 100); // set the motor speed: if (motorSpeed > 0) { myStepper.setSpeed(motorSpeed); // step 1/100 of a revolution: myStepper.step(stepsPerRevolution / 100); } if (t - t0 > 1000) { long curPos = myEnc.read(); speed = abs(curPos-lastPos); long pos = map(speed,0,15,0,180); myservo.write(pos); lastPos = curPos; lcd.setCursor(0, 0); lcd.print( "i: " + String(map(analogRead(PHOTORESISTOR), 0, 1023, 0, 99)) + " m: "+String(motorSpeed)); lcd.setCursor(0, 1); lcd.print(" " + String(speed)+ " o: " + String(map(pos,0,180,0,99))); // print the number of seconds since reset: t0 = t; } } }
]]>
For the middle step on our transducer, we were deciding between having a servo drive a potentiometer, having a servo drive a platform with an accelerometer, and having a speaker output measured by a microphone. We ended up deciding on the servo – accelerometer pair, but also made a servo – potentiometer prototype as an initial version to test various components on.
This was the initial double transducer design we had. My project was barely held together. I had to tape the micro servo to the Maker Cards and used the wire cutter to hold the breadboard down so that the board wouldn’t lift when the servo motor turned the potentiometer.
Using an ultrasonic ranger, I detected the distance from the build. The ranger determined the angle of the servo motor which was attached to the potentiometer. This potentiometer then determined the brightness of the LED.
By replacing the small breadboard with a bigger version, I was able to make a less cluttered double transducer.
Here is a video of my working double transducer. I displayed the input and output values using the Serial Monitor on Arduino. Through the video, you can see that the light turns brighter when the box is closer to the ultrasonic sensor and dimmer when the box is further away.
Similar to Erica’s version, I connected the servo to the potentiometer through a paperclip and servo mount.
Due to the noisiness of the ultrasonic data, I added a moving average filter to smooth that out. Also, I found out the analog input pins do not have PWM capabilities which was a helpful reference for later.
We soldered the 3-axis accelerometer and a terminal, ensuring a strong connection for our middle step in the transducer. The final configuration of the objects on the board was tightly put together, with the 3-axis accelerometer acting like a protective hood over the terminal.
For the final project, I also configured the LCD to display the outputs. Working with the LCD was difficult because of the intense amount of wiring, but through following tutorials online, I was able to successfully have the LCD display our input and output values.
To convert the accelerometer’s 3-axis acceleration readings into an angle value that could be interpreted as a parallel to the servo’s rotation, there was a little trig involved (taking an inverse tangent of two axes, modeled after the unit circle). The above clip is me testing the accelerometer’s analog inputs and figuring out the range of raw inputs to map to -1 and 1 for both x and y values on the unit circle.
For the final transducer, I soldered part of the proto-board that the accelerometer attaches to and mounted the the board onto the servo arm via paper clips.
/* * 60-223, Project 1 * Eric Zhao (ezhao2), Erica Fu (efu) * time spent: 20 hours * * Collaboration and sources: * 1) Eric and Erica worked together from planning, coding, to building the final project * 2) Zach assisted many times throughout the process * 3) We borrowed heavily from the Arduino guide to use the LiquidCrystal library * website link: https://www.arduino.cc/en/Tutorial/LibraryExamples/HelloWorld * 4) We implemented this moving average filter to smooth out ultrasonic data values: * https://courses.ideate.cmu.edu/60-223/s2021/tutorials/code-bites#exponential-moving-average-filter * Challenge(s): Since there are many wires, the wiring often became jumbled and confusing. * Also, the LCD display could only use certain pins, causing us to have to rewire the whole project. * The connection from the servo motor to the 3-axis-accelerometer was also quite difficult since * we had to customize parts and eventually settle for using a paperclip to make a mechanical connection. * * Next time: We will organize wiring better so that the circut is clearer so that any change * of wiring will be less confusing. *To prevent breaking any more Arduinos, Erica will double check the wiring to ensure that * there is minimal damage caused in the future. * * Description: The entire breadboard is mounted to a servo motor whose output shaft is connected to a * rigid vertical structure. Depending on the input voltage from the ultrasonic sensor, the servo motor * will output a corresponding angle, causing the entire breadboard to rotate. An accelerometer attached * to the breadboard will read the tilt angle of the breadboard caused by the servo. The input voltage * will determine the output voltage of the LED. * * Pin mapping: * * Arduino pin | type | description * ------------|--------|------------- * A0 input xInput of the 3-axis accelerometer * A1 input yInput of the 3-axis accelerometer * A2 input zInput of the 3-axis accelerometer * 9 output mini servo motor * 6 output red LED * 12 output rs pin for the LCD monitor * 11 output en pin for the LCD monitor * 5 output d4 pin for the LCD monitor * 4 output d5 pin for the LCD monitor * 3 output d6 pin for the LCD monitor * 2 output d7 pin for the LCD monitor * */ #include <Servo.h> #include <NewPing.h> #include <LiquidCrystal.h> #define TRIGGER_PIN 8 #define ECHO_PIN 7 #define MAX_DISTANCE 75 NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); const int waitTime = 250; long prevTime = 0; const int xInput = A0; const int yInput = A1; const int zInput = A2; const int SERVOPIN = 9; const int LED = 6; Servo SERVO; float filtered_val = 0; // this variable, of type float (decimal), holds the filtered sensor value const float PREV_WEIGHT = 0.8; // this variable, of type float (decimal), determines the previously filtered value's weight. const float CUR_WEIGHT = 1 - PREV_WEIGHT; // this variable, of type float (decimal), determines the current sample's weight. const int SENSOR_PIN = A0; // this variable, of type int, determines the sensor's pin. // initialize the library by associating any needed LCD interface pin // with the arduino pin number it is connected to const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); void setup() { // put your setup code here, to run once: pinMode(xInput, INPUT); pinMode(yInput, INPUT); pinMode(zInput, INPUT); pinMode(LED, OUTPUT); SERVO.attach(SERVOPIN); Serial.begin(115200); // set up the LCD's number of columns and rows: lcd.begin(16, 2); } void loop() { // put your main code here, to run repeatedly: delay(50); //raw data by axis from accelerometer int x = analogRead(xInput); int y = analogRead(yInput); int z = analogRead(zInput); //data from accelerometer, mapped to units -1000 to 1000 int xVect = map(x, 260, 390, -1000, 1000); int yVect = map(y, 250, 380, -1000, 1000); int zVect = map(z, 320, 385, 0, 1000); //below variables are the above vars but divided by 1000 to achieve unit circle float xVectUnbound = xVect/1000.0; float yVectUnbound = yVect/1000.0; float zVectUnbound = zVect/1000.0; //constraining final readings to unit circle -1 to 1 float xVectFinal = constrain(xVectUnbound, -1, 1); float yVectFinal = constrain(yVectUnbound, -1, 1); float zVectFinal = constrain(zVectUnbound, 0, 1); //final arctan value (takes two axes and converts into degrees) float degreeVal = atan2(xVectFinal, zVectFinal) * 180/PI; //creates a weighted reading float cur_reading = sonar.ping_cm(); if(cur_reading != 0){ filtered_val = filtered_val * PREV_WEIGHT + cur_reading * CUR_WEIGHT; } //takes a smoothed out sonar reading and converts to servo degrees int servoVal = map(filtered_val, 0, 75, 0, 180); //takes accelerometer degree reading and converts to int outputVal = map(degreeVal, -55, 90, 0, 255); if(cur_reading != 0 && servoVal > 0){ SERVO.write(servoVal); } if(outputVal > 0){ analogWrite(LED, outputVal); } int inputDisplay = map(filtered_val, 0, 75, 0, 100); int middleActDisplay = map(servoVal, 0, 180, 0, 100); int middleSensorDisplay = map(outputVal, 0, 255, 0, 100); if(millis() - prevTime > waitTime){ //outputs data to LCD display 4 times a second lcd.setCursor(0,0); lcd.print((String)"i:"+ inputDisplay); lcd.setCursor(5,0); lcd.print((String)"m:" + middleActDisplay); lcd.setCursor(7,1); lcd.print(middleSensorDisplay); lcd.setCursor(11,1); lcd.print((String)"o:" + middleSensorDisplay); prevTime = millis(); } }
We found a lot more issues throughout the span of this project than either of us imagined would happen! Different libraries conflicted with each other (for example, the Servo library shut down certain pins that the NewPing ultrasonic library utilized). Mechanical connections between certain parts were difficult to make robust enough to handle the servo’s torque; we ended up using paperclips for a lot of these as compared to tape, which was a lot weaker.
]]>The potentiometer in this video is representative of our joystick middle step.
Tate’s final board
My final version had correct transduction and displayed values at good fidelity, but even with an added transistor to the vibration motor it drew so much current that it would dim the LCD screen, but otherwise, this version had a functioning double transduction.
Dominique’s final board
At first I thought my final version had some wiring issues surrounding the vibration motor, but upon closer inspection, and the addition of a replacement vibration motor, my double transducer actually worked fine. The LCD sensor and actuator values were being displayed properly, and luckily, it did not dim when the vibration motor was triggered, and I did not use a transistor, so this was a peculiar but happy outcome.
At first our LCD had many display issues. First the backlight would not turn on, then the values flashed too fast, and so forth. After implementing a special delay in void loop and switching libraries, we were able to fix the problems, at least on one of our prototypes.
Servo-joystick prototyping
We used our two prototypes to test different things, Dominique’s explored the LCD more and mine focused on the mechanical interface from servo to the joystick.
Hopefully, you can hear the vibration motor buzzing in this video as I move the flashlight closer to the photoresistor on the other end of the board. And you can see the middle step (servo moving a joystick) as well. I had to cover the photoresistor with my hand in order to block out ambient light (that was really messing with the sensory data because it was a very bright room and my flashlight was dying), so you can’t quite see the flashlight action, but it’s happening, please trust me.
Our double transducer first takes in light in a room and records the brightness through the Arduino. That value is then used to determine the rotation degree of a servo motor; more light equals a higher degree up within a specified range. The servo pushes on a joystick, from which a y-Axis position is read and stored in Arduino. Finally, a vibration motor receives that output and vibrates more as the y-axis position increases.
LCD hieroglyph issues
I struggled a lot with my LCD displaying odd values because of the vibration motor current draw creating noise in the circuit.
Servo-Joystick interface and soldering to protoboard
I also struggled a lot with soldering, as I accidentally grabbed a non-powered protoboard so after soldering my adapters for the servo and joystick I had a long period of debugging to find the cause of that. It was good soldering practice because I had to bridge connections with quarter-inch lengths of wire.
Prototyping popsicle stick connection
The hardware for this circuit was actually relatively simple to build, at least on the breadboard. The hard part was configuring the delicate servo to properly trigger the joystick. So as you can see here, that problem has not been solved yet.
LCD prototyping
This was when the LCD still was not working properly. As you can see, it is not displaying anything, and that’s due to a number of reasons which are explained in detail in the Discussion section. The biggest problem here was that the LCD was wired incorrectly.
Even though we went into the project anticipating the hardware components for wiring a transducer that involved a joystick would be complex, that part ended up not being that difficult and only took a short amount of time to configure.
As far as the difficult aspects of the project, we ran into a lot of hiccups with writing to the LCD. One of the issues was especially mundane and we did not realize it; for the longest time, the LCD would not display any coherent information, despite the code looking to be fine. After consulting with Zach, we discovered that we were not using the right library the whole time. We were using commands from LiquidCrystal_I2C and but the LCD we have does not have the I2C backpack. So not only did we not realize we had a different LCD than the tutorials, we were wiring it wrong. Anyway, we sorted this, fortunately, but the LCD on one of the two double transducers we built still had issues we could not troubleshoot.
We also had a strange issue where as our joystick value went up, the vibration intensity went down. We wanted both to increase in direct proportion. Turns out there was a typo for one of our map() functions where the value was reversed. Also we ended up writing a bit of extra code that constrained the max value of joystickVal which helped to keep our values in line.
Tinkercad also had its own set of issues. because some inputs could not be sensed by Tinkercad, we had to “fake it.” It was interesting figuring out how to do this, but ultimately not too challenging. Since the first input is light, we could simply make that value increase and decrease automatically and the rest of the sensors and actuators respond accordingly. Because there’s no joystick in Tinkercad, we replaced it with a potentiometer, just to clarify.
There’s also the issue of figuring out how to get the tiny servo arm to push the joystick, which does not budge very easily. We used superglue to glue half a popsicle stick to the arm, as you can see in the photos, and we taped down the motor, which did help. The motor on one transducer pushed sideways on the joystick, and the motor on the other was configured to push downward, and both seemed to work fine.
Finally, for one of the two double transducers we built, it unfortunately broke after working properly a few times. The cause of the break is still kind of unknown even after checking the serial monitor and wiring. Because it was only the vibration motor that stopped responding even though the vibration value was displaying appropriately, we figured it was a wiring issue, one of the complications from a less-than-stellar soldering technique. It did look like the vibration wires were burned and not very attached, but after replacing the vibration motor and still finding issues, we figured the problem was deeper in the hardware. That was until I switched power sources and everything was completely fine. I agonized for hours over this very simple issue (Dominique).
For future projects it certainly would be useful to get a better grasp on soldering with a steady hand. That did not seem to be my strong suit, and I’m sure it will be a useful skill for the future (Dominique).
Block diagram
Schematic drawing
/* Double Transducer: Light to Position to Movement Dominique Aruede and Tate Johnson Description: Takes in a light value which informs the degree rotation of a servo arm which pushes a joystick which informs the vibration intensity of a vibration motor. Pin mapping: Arduino pin | type | description ------------|--------|------------- A0 input photoresistor for initial input A2 input reads joystick x axis (only needed in this sketch to keep the y axis stable) A3 input reads joystick y axis A4 input reads vibration motor output 2 output writes to pin D7 on LCD 3 output writes to pin D6 on LCD 4 output writes to pin D5 on LCD 5 output writes to pin D4 on LCD 6 output writes to vibration motor 8 output writes to servo motor 11 output writes to enable pin on LCD 12 output writes to RS pin on LCD Collaboration and sources: - https://www.arduino.cc/en/Tutorial/LibraryExamples/HelloWorld - https://canvas.cmu.edu/courses/22031/modules (photoresistor asynchronous lecture) */ #include <Wire.h> #include <LiquidCrystal.h> #include <Servo.h> //pin constants const int PHOTORESPIN = A0; const int SERVOARMPIN = 8; const int JOYSTICKPINx = A2; const int JOYSTICKPINy = A3; const int VIBRATIONPIN = 6; const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2; const int WAIT = 300; unsigned long timerVariable = 0; int lightVal = 0; int armVal = 0; int joystickVal = 0; int vibVal = 0; /* Create an LCD display object called "screen" with I2C address 0x27 which is 16 columns wide and 2 rows tall.*/ LiquidCrystal lcd(rs, en, d4, d5, d6, d7); Servo arm; // make a servo object void setup() { // initiate pins pinMode(PHOTORESPIN, INPUT); pinMode(JOYSTICKPINy, INPUT); pinMode(VIBRATIONPIN, OUTPUT); // servo is plugged into the assigned pin on Arduino arm.attach(SERVOARMPIN); //LCD setup lcd.begin(16, 2); } void loop() { //digitalWrite(VIBRATIONPIN, HIGH); //convert light sensing to right range lightVal = analogRead(PHOTORESPIN); int LCDlightVal = 0; LCDlightVal = map(lightVal, 0, 1023, 0, 99); //write to the transducer first part armVal = map(lightVal, 0, 1023, 0, 170); int LCDarmVal = 0; LCDarmVal = map(lightVal, 0, 1023, 0, 99); arm.write(armVal); //read the second transducer part joystickVal = analogRead(JOYSTICKPINy); if (joystickVal >= 512) { joystickVal = joystickVal - 512; } else { joystickVal = 0; } int LCDjoystickVal = 0; LCDjoystickVal = map(joystickVal, 0, 512, 0, 99); // write to output vibVal = map(joystickVal, 0, 512, 0, 255); analogWrite(VIBRATIONPIN, vibVal); int LCDvibVal = 0; LCDvibVal = map(vibVal, 0, 255, 0, 99); if (millis() - timerVariable > WAIT) { lcd.clear(); lcd.home(); lcd.print("i:"); lcd.print(LCDlightVal); lcd.print(" m:"); lcd.print(LCDarmVal); lcd.setCursor(8, 1); lcd.print(LCDjoystickVal); lcd.setCursor(12, 1); lcd.print("o:"); lcd.print(LCDvibVal); timerVariable = millis(); } }
]]>
James’ final build
Amelia’s final build
Taped connection between vibration motor and accelerometer for best transfer of signal
Cover for the light sensing part of transducer to get rid of noise from environment
Slot restricting the motion of Popsicle stick to mostly linear movement to create a change in distance
A vibrator is attached to a sensor which senses the rate of vibration. Based on this vibration, a light is turned on underneath a box. How much the vibrator vibrates determines how bright the light gets. So if the vibrator vibrates a lot, the light gets really bright, and if the vibrator does not vibrate a lot, the light does not get as bright. The brightness of the light is captured by another sensor which senses how bright the light is. The sensor then tells the computer how much to turn a motor to move a stick. So if the light is really bright, the motor is told to move the stick very far, and if the light is not as bright, the motor is told to move the stick not as far. The states of all of these inputs and outputs (i.e. the level of change) is fed to a display that shows how much each step has changed.
Diagnosing the noise in the analog input pins with an oscilloscope
Vibration motor connected to two wires with male ends to make better and more maneuverable connection to bread board
One of the many ways we tried to sense vibration frequency. Here we are trying to measure the duty cycle of the PWM type input from the vibration motor.
Putting together the distance component for the first time to get the layout
Throughout the development process, the easiest part was definitely the planning stage. The stage where we brainstormed three different ideas and paid little attention towards the actual build. It was a time where we let our imaginations run wild and had little to debug since the projects weren’t fully implemented, only idealized. Once we finally got to the building and coding stage however, we found implementing past the planning phase is much harder. It involved having to build the circuit while making sure none of the wires had loose connections, building a connection between the vibration motor and Arduino which meant stripping two male- to-female wires as well as the vibration motor and securing them together with athletic tape (neither of us had electrical tape on standby), measuring the value of the accelerometer with a broken accelerometer (which we only later realized), soldering an LED and photoresistor, building a linkage system out of popsicle sticks, and figuring out the code to tie these pieces together. All of these steps came with their own frustrating challenges that allowed us to grown our knowledge of sensors and Arduino development
The most frustrating thing that we learned was that analog input pins on the Arduino can pick up a lot of noise from other parts of the board. In particular, adjacent analog input pins can read signals from each other almost as if they were connected. In our case, we thought the accelerometer was wired to the Arduino and we were reading a signal from it, but it turned out to not be connected to the Arduino at all. Despite this fact, we still read an analog input from that pin and when compared to the signal produced by the potentiometer, the signals matched exactly in how they changed with input into the potentiometer. Instead of reading the accelerometer data, we were actually reading ghost signals from the adjacent potentiometer input pin which was only solved by moving the accelerometer input pin to the opposite side of the analog input pin array.
We also learned the importance of data structures in signal processing, especially when performing arithmetic operations. While trying to write values to our stepper motor, we came into a problem where we couldn’t get our code to run for more than 1 loop. This was caused by the way in which we were storing the variables used to calculated step count. We chose to store the all our variables as type “int” by default which meant that it would round down to the nearest integer when it stored the step value. The some of the values we were setting as the used to calculate the number of steps, however, were on a scale of 0 to π which meant that they were frequently in between 0 and 1. Because these values were rounded down even if they were 0.9, this meant that we ended up with division by zero causing our program to break and only run once. Storing the variables used to calculate step number solved this problem.
Looking back now, there are several factors that would’ve changed the direction of the project completely. For instance, the first time we met to work on the project was the Sunday before the project was due because we thought it was plenty of time before the deadline. This was a mistake on our part and we should’ve been more proactive about meeting before Sunday (i.e. on Friday and Saturday). This would’ve given us more time to realize where we were going wrong and what we could’ve done to fix the problem. Another factor would’ve been a better sensor to detect the frequency of the vibration coming from the vibration motor. We noticed that the accelerometer was pretty noisy and we had trouble reading the sensor. Maybe these changes would have improved our project, or possibly caused new problems, but these are just a few we’ve chosen to reflect on.
/* 60-223, Project 1 Amelia Lopez (aelopez) James Kyle (jkyle1) Time spent: 8 hours Description: Takes in a vibration input, transforms that analog input value into an LED brightness, which is then transformed into a distance controlled by the stepper motor. Vibration input is read through an accelerometer and LED intensity value is read through a photoresistor. Challenges: The stepper motor isn't responding to the photoresistor's value as we intended, so that half of the code is buggy. Since the vibration motor works on a pulse, this causes the light to blink fairly fast but it still dims/brightens as a result of the vibration motor speed. Next time: Definitely take more time to understand what went wrong between the photoresistor value and the stepper motor desired output. I think looking at the Arduino libraries is a good place to start with this because maybe someone else had the same problem and published their solution for open source use that we can cite and tweak to the specs of our project Collaboration and Sources: 1) For the accelerometer readings (line 163-167), used code from https://tinyurl.com/Accelerometerlink 2) For LCD display, used code from http://www.arduino.cc/en/Tutorial/LiquidCrystalHelloWorld 3) For stepper motor, used code from https://tinyurl.com/StepperMotorLink Inputs: Potentiometer (which varies vibration motor rate) In-between: LED Output: Stepper motor, LCD display Pin mapping: Arduino pin | type | description ------------|--------|------------- A0 input potentiometer A1 input X-pin Accelerometer A2 input Y-pin Accelerometer A3 input Z-pin Accelerometer A4 input photoresistor 13 output Stepper Motor Driver IN1 12 output Stepper Motor Driver IN2 ~11 output Vibration motor ~10 output Stepper Motor Driver IN3 ~9 output Stepper Motor Driver IN4 8 output LCD RS 7 output LCD E ~6 output LED ~5 output LCD D4 4 output LCD D5 ~3 output LCD D6 2 output LCD D7 (digital PWM~) LCD circuit: LCD RS pin to digital pin 8 LCD Enable pin to digital pin 7 LCD D4 pin to digital pin 5 LCD D5 pin to digital pin 4 LCD D6 pin to digital pin 3 LCD D7 pin to digital pin 2 LCD R/W pin to ground LCD VSS pin to ground LCD VCC pin to 5V 10K resistor: ends to +5V and ground wiper to LCD VO pin (pin 3) */ #include <Stepper.h> #include <LiquidCrystal.h> //DECLARING ARDUINO PINS const int POTENT = A0; const int ACCE_X = A1; const int ACCE_Y = A2; const int ACCE_Z = A3; const int PHOTORES = A4; const int STEP_IN1 = 13; const int STEP_IN2 = 12; const int VIBRATION_MOTOR = 11; const int STEP_IN3 = 10; const int STEP_IN4 = 9; const int LCD_RS = 8; const int LCD_E = 7; const int LED_PIN = 6; const int LCD_D4 = 5; const int LCD_D5 = 4; const int LCD_D6 = 3; const int LCD_D7 = 2; //DECLARING INTS FOR ACCELOREMETER AND LED int accel_xVal = 0; int lightVal = 0; //DECLARING MISC. CONSTANTS USEFUL TO STEPPER MOTOR const int maxAnalogValue = 1023; const int stepsPerRevolution = 64; //fits the specification of our motor const int motorSpeed = 20; float currentDistance = distOfString / 2; //PRECONDITION: OBJECT HAS TO BE HALFWAY int targetDistance; float steps; bool stepMotor = false; //DECLARING INTS FOR LCD WAITING TIMES const int WAIT_TIME = 300; unsigned long timeVariable = 0; // initialize the library by associating any needed LCD interface pin // with the Arduino pin number it is connected to LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7); Stepper myStepper(stepsPerRevolution, STEP_IN1, STEP_IN2, STEP_IN3, STEP_IN4); void setup() { //INPUT PINS pinMode(POTENT, INPUT); pinMode(VIBRATION_MOTOR, OUTPUT); pinMode(ACCE_X, INPUT); pinMode(ACCE_Y, INPUT); pinMode(ACCE_Z, INPUT); pinMode(PHOTORES, INPUT); //OUTPUT PINS pinMode(LED_PIN, OUTPUT); pinMode(VIBRATION_MOTOR, OUTPUT); //MOTOR functions myStepper.setSpeed(motorSpeed); // set up the LCD's number of columns and rows: lcd.begin(16, 2); //print constant letters on screen lcd.print("i:"); lcd.setCursor(6, 0); lcd.print("m:"); lcd.setCursor(8, 1); lcd.print("0"); lcd.setCursor(12, 1); lcd.print("o:"); } void loop() { //READING POTENTIOMETER VALUE //POTENTIOMETER VALUE DETERMINES VIBRATION MOTOR RATE int potentValue = analogRead(POTENT); // 0 <= potentValue <= 1023 int vibrationStrength = map(potentValue, 0, 1023, 0, 153); // 0 <= vibrationStrength <= 155 analogWrite(VIBRATION_MOTOR, vibrationStrength); //using PWM int xVal = analogRead(ACCE_X); //ACCELEROMETER MEASURES VIBRATION MOTOR RATE (x-axis) //RATE MEASURED IS USED TO LIGHT UP AN LED A CERTAIN INTENSITY int oldX = accel_xVal; accel_xVal = analogRead(ACCE_X); int dif = abs(oldX - accel_xVal); int lightVal = map(dif, 0, 520, 0, 255); analogWrite(LED_PIN, lightVal); //PHOTORESISTOR TAKES IN LED INTENSITY VALUE //PHOTORESISTOR VALUE USED TO CHANGE STEPPER MOTOR int photoValue = analogRead(PHOTORES); // 0 <= potentValue <= 1023 int lowbound = 700; //bright light int highbound = 990; //no light //int stepVal = map(photoValue, lowbound, highbound, targetDistance = map(photoValue, 850, 1000, 0, 60); //Convert photoresistor value into desired distance into rotations for stepper motor if (targetDistance != currentDistance) { steps = (targetDistance - currentDistance) * stepsPerRevolution / 360; currentDistance = targetDistance; stepMotor = true; } //Setting step stepper motor to desired distance if (stepMotor == true) { myStepper.step(steps); delay(500); //Wait 0.5s for motor to finish moving stepMotor = false; } //UPDATING VALUES ON LCD if (millis() - timeVariable > WAIT_TIME) { //updating input sensor value lcd.setCursor(2, 0); lcd.print(map(xVal, 0, 525, 0, 99)); //updating middle step actuator lcd.setCursor(8, 0); lcd.print(map(lightVal, 0, 255, 0, 99)); //updating middle step sensor value lcd.setCursor(8, 1); lcd.print(map(photoValue, lowbound, highbound, 0, 99)); //updating output actuator value lcd.setCursor(14, 1); lcd.print(map(steps, 0, currentDistance, 0, 99)); delay(200); timeVariable = millis(); } }
]]>