Peter Heo, John Hewitt, Evangeline Mensah-Agyekum
Gallery
View of Completed Project
View of Servo Motor Arm and Round FSR
Progress Images
Initial design for pressure actuation onto round FSR. Use of a shorter arm compared to final project.
Second version of pressure actuation using servo motor. Used a sort of piston to press down on the round FSR; however, it was finicky and not very stable.
I had everything ready to be soldered, but I didn’t realize the wires weren’t going to be long enough
my broken FSR
Discussion
In terms of difficulty, the brainstorming was one of the more easier parts, as we were able to quickly come up with what we wanted for our middle step to be. However, what was more difficult were the more specific mechanics of the middle step, as we were initially unsure of how to convert slide position into pressure. We started with a couple of ideas but we were able to narrow it down to an arm that would press down on the sensor. Peter tried to come up with a piston that would press and lift a weight onto the round FSR, and a sort of linear actuator that would press against a vertically placed FSR sensor, but both of these proved to be unwieldy and unreliable. We eventually settled on using a straw attached to a lever (servo arm) with enough flexibility to bend when pressed against the FSR.
There were also a couple of challenges along the way, as the code proved to be one source of difficulty. The motor would jitter and stutter periodically, making the servo arm unreliable in pressing down on the FSR sensor. The initial approach to this problem was try to smooth out the values, as we thought it was the potentiometer sending jumpy values, but we learned that it was actually timer collisions between the servo motor and the Neopixel LED ring causing the servo motor to jitter. From this, we learned that sometimes it is not the main user code that is unreliable, but the type of libraries and mechanical components we use.
It was also interesting to calibrate the sensors to not only work with each other, but fit into a 1-100 range on the lcd. It took some finessing, but as we messed around with the values it began to feel more intuitive.
Both Evangeline and Peter experienced a specific wiring issue with the potentiometer, where they switched up input with ground/5V wiring, causing a short circuit. However, after learning that the wiring was fixed, this problem was easily addressed. Soldering also proved an interesting step for Peter, as he was unfamiliar with it, and learning how to solder was something he enjoyed learning, albeit a bit nervously. John preemptively soldered up the different parts of the project without realizing the wires were not long enough to reach the opposite ends of the board, and had to re-solder things. John also snapped a round FSR when trying to reposition it, it was interesting to use such a delicate piece. John also learned to what a heatsink was, and would have probably damaged the FSR if he hadn’t been warned ahead of time.
This project also made us think more spatially, as organization and how components would be physically laid out on the board were also part of the project near the end of putting the whole thing together. However, the straightforwardness of the setup and its processes made it easier to assemble.
Block Diagram and Schematic
Code
//Project: Double Transducer (Position to Brightness) //Peter Heo //John Hewitt //Evangeline Mensah-Agyekum //The code below allows for the user to control a slide potentiometer, which then controls a servo motor. //The slide potentiometer readings are converted to a servo motor angle. //Said servo motor exerts pressure onto a round FSR through the use a physical arm. //The round FSR readings are then converted to a certain degree of brightness for an Neopixel LED ring. //All the input and output values of the mechanisms aforementioned are displayed onto an LCD screen... //...all mapped to a range of 0-99. //Pins Mode Description //A0 Input Slide potentiometer readings //A3 Input Round FSR readings //6 Output LED Ring //10 Output Servo motor //Library and implementation for the Neopixel servo motor object: //https://learn.adafruit.com/neopixels-and-servos/the-ticoservo-library #include <Wire.h> #include <LiquidCrystal_I2C.h> #include <Adafruit_TiCoServo.h> #include <Adafruit_NeoPixel.h> //Intitialize pin for LED ring and the number of lights on said LED ring, as well as the LED object. #define PIN 6 #define NUMPIXELS 16 Adafruit_NeoPixel light = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); //Initialize pins for the slide potentiometer, the round FSR, and the servo motor, as well as the LCD screen object. LiquidCrystal_I2C screen(0x27, 16, 2); const int SLIDEPIN = A0; const int PRESSUREPIN = A3; const int SERVOPIN = 10; //Initialize the time between display updates, and the timer itself. const int LCDUPDATETIME = 250; long prevDisplayTime = 0; Adafruit_TiCoServo servoMotor; void setup() { //Set up pins for inputs and outputs: //Slide potentiometer... //...Servo motor... //...and Round FSR sensor. pinMode(SLIDEPIN, INPUT); servoMotor.attach(SERVOPIN); pinMode(PRESSUREPIN, INPUT); //Set up LCD display. screen.init(); screen.backlight(); screen.display(); //Set up light. light.begin(); light.setBrightness(0); light.show(); //Open the serial communication port to talk back to the computer. Serial.begin(9600); } void loop() { //Look at current time. long currentTime = millis(); //Initialize slide potentiometer value. int slideVal = analogRead(SLIDEPIN); //Range should be about 11 degrees. //Set servo motor angle proportional to slide pot value. int servoVal = map(slideVal, 0, 1023, 142, 156); servoMotor.write(servoVal); //Read pressure value from FSR sensor. int pressureVal = analogRead(PRESSUREPIN); //Set brightness of LED ring proportional to pressure value. int brightnessVal = map(pressureVal, 0, 410, 0, 255); light.setBrightness(brightnessVal); //Set color of all LEDs on the LED ring to green. for (int i = 0; i < NUMPIXELS; i++) { //Set color of all LEDs on the LED ring to green. light.setPixelColor(i, light.Color(128, 150, 128)); } light.show(); //Update LCD display every 250 milliseconds. if (currentTime - prevDisplayTime >= LCDUPDATETIME) { screen.clear(); //Print slide values on a range of 0-99 on LCD display. int slideDisplayVal = map(slideVal, 1023, 0, 0, 99); screen.home(); screen.print("i:"); screen.print(slideDisplayVal); //Print servo motor value on a range of 0-99 on LCD display. int servoDisplayVal = map(servoVal, 142, 156, 99, 0); screen.setCursor(6, 0); screen.print("m:"); screen.print(servoDisplayVal); //Print pressure values on a range of 0-99 on LCD display. int pressureDisplayVal = map(pressureVal, 0, 410, 0, 99); screen.setCursor(8, 1); screen.print(pressureDisplayVal); //Print brightness values on a range of 0-99 on LCD display. int brightnessDisplayVal = map(brightnessVal, 0, 255, 0, 99); screen.setCursor(12, 1); screen.print("o:"); screen.print(brightnessDisplayVal); prevDisplayTime = currentTime; } }
]]>
Overall Photo of Matthew’s Transducer
Overall photo of Shenai’s Transducer
This three-axis magnetometer senses the strength of a magnetic field in 3 dimensions.
Shown is the Stepper Motor Driver. This provides the “brains” of the stepper, controlling direction, rotation angle and speed the motor rotates at.
The Stepper Motor is attached to a rack and pinion system that slides back and forth depending on how strong the magnetic field is. The system slides on a piece of acrylic to reduce the kinetic friction between the 3-D printed material and the cardboard surface.
The Ultrasonic Ranger detects how far the rack is using sonar, and sends the signal back to the Arduino. The ranger is soldered to a soldering board with a screw terminal soldered on behind it, making it easy to connect wires since they don’t move around as our transducer is at work.
The Arduino maps the distance measurement to an angle and instructs the Servo Motor to rotate to the specific angle accordingly.
The rotation of the Servo Motor reveals a part of the color gradient as it rotates. This color output is then read by the next group’s transducer.
Project Video
We wanted to take input from the forces produced by a magnet, and give that information to a rotating motor. Then, the motor would rotate a gear, which would move a horizontal block with ridges that fit into the gear. We then sensed the position of the horizontal block, and then gave that information to another rotating motor. This second rotating motor was attached to a curved piece of paper, which had a range of colors on it. Our final output was the outward-facing color on the paper. All the information about positions and level of magnetic strength were printed as numbers on a screen for the viewer to see.
We used SolidWorks to model a rack and pinion we could 3-D print. The assembly was designed to move 1 cm for each 1/10th rotation of the motor, meaning the teeth on the rack are spread apart by 1 cm, the same measuring resolution as the ranger.
We utilized the IDEATE skylab to 3-D print the parts we needed for our project in a timely fashion.
process stepper wiring, was wired to smaller motor so we had to redo
We created the printed color using a semi-cylinder with a color gradient surface. We used a popsicle stick as the axel so we could easily incorporate the servo motor into our design. We changed to a gradient from a rainbow pattern to allow us to print any hue value that the magnetic field strength is mapped to.
We both learned a lot from this process. We enjoyed teaming up together because we were equally invested in the success of the project, and because both had (semi-overlapping but still) different areas of technical expertise, which allowed us to synthesize knowledge from our respective fields. For example, Matthew is a Mechanical Engineering major and really good at CAD, so we were able to make quality, physical mechanisms within our middle step via our rack and pinion. Shenai is an Electrical and Computer Engineering major and she tackled a lot of the wiring and programming challenges.
Comments from Shenai: I enjoyed the team aspect of this project, as well as the hands-on quality of the work, which was something I had been lacking in my other classes. I also felt that I grew as a debugger, both in code and circuitry. It was really satisfying when I spotted that Matt’s stepper motor wasn’t properly grounded, and was able to fix it quickly. Another super satisfying moment was when I combined our separate code into a single file, very quickly checked for timing issues/duplicated pins and variables, and it compiled and ran perfectly on the first try.
However, we had a terrible time with the stepper motor. We had to go through 3 different chips and 2 motors, and we had to discard our custom-made 3-D print that housed the smaller motor because the smaller motor wouldn’t work properly with any of the chips. The 3-D print made us want to hang onto the smaller motor because we had already put so much time into setting up our machine specifically for it, but finally we ended up pivoting to the more powerful and reliable motor. I think if we had been more willing to pivot entirely instead of changing little things to fit our past decisions, we would have breezed through this process faster. Although, I will say, I got really fast at unwiring and rewiring various stepper motors and their drivers, so this iterative process was helpful for my technical growth in that respect.
Comments from Matthew: It was very exciting to have the opportunity to apply my knowledge of mechanical systems and CAD into this project. Despite having to change which stepper motor we used, the parts I created for the project all worked without the need for iterative design and further prototypes. This saved the course budget some money as we didn’t have to keep printing and printing more materials. Together, we were able to synthesize electrical and mechanical design in an effective and creative way to produce a successful end result. In addition to applying my Mechanical Engineering skills, I was able to learn a lot on the electrical side as well. I was able to brush up my coding skills in Arduino, become familiar with different electrical connections and sensors (magnetometer, ultrasonic ranger, stepper and servo motors, etc), and I even got to learn how to strip and solder wires for the first time. I feel that the acquisition of these new skills will help me grow as an engineer and will allow me to be more versatile in upcoming projects.
Functional Block Diagram of our Project using diagrams.net
Electric Schematic for our project. We had access to a more simple LCD Display which allowed us to use SDA and SCL pins for both the magnetometer and the LCD display.
Code:
]]>/* * Magentic Field to Color Gradient Double Transducer * * By: Matt Wagner and Shenai Chan * * Desc: Takes in a reading of magnetic field strength, * converts it to rotational position on stepper motor, * uses Ultrasonic Ranger to sense the position of a * rack driver by the motor, feeds that information * to rotational position on a servo motor with substantial * delay to allow for proper reading of the servo position. * Sends input/output/intermediate information to an LCD display. * * Pin mapping table: * SDA & SCL connected to both LCD and 3-axis magnetometer * 6 <-> servo * 8 <-> dir on stepper motor * 9 <-> step on stepper motor * 11 <-> echo on ultrasonic ranger * 12 <-> trig on uultrasonic ranger * * Credit: * Referenced starter code by Robert Zacharias on timing signal sends; * instantiating USR, LCD, and servo; and writing to LCD. * * https://courses.ideate.cmu.edu/60-223/f2021/tutorials/code-bites * https://courses.ideate.cmu.edu/60-223/f2021/tutorials/I2C-lcd * * Referenced docs from Patrick Wasp's AccelStepper lib & * MPrograms' compass lib */ /* First, import some libraries: */ // I2C Library #include <Wire.h> // For the 3-axis magnetometer #include <QMC5883LCompass.h> // For the stepper motor #include <AccelStepper.h> // For the ultrasonic ranger #include <NewPing.h> // For the Servo Motor #include <Servo.h> // For the LCD display #include <LiquidCrystal_I2C.h> /* Then, instantiate objects that correlate to wired devices */ // Make LCD obj LiquidCrystal_I2C screen(0x27, 16, 2); int iVal; int mVal1; int mVal2; int oVal; // Make magnetometer obj QMC5883LCompass compass; // Make stepper obj, as well as corresponding variables for waits // between writes, and keeping track of current position #define dirPin 8 #define stepPin 9 #define motorInterfaceType 1 const int wait = 1000; long timer = 0; AccelStepper stepper = AccelStepper(motorInterfaceType, stepPin, dirPin); int currentPos; // Make USR obj #define TRIGGER_PIN 12 #define ECHO_PIN 11 #define MAX_DISTANCE 200 NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // Make servo obj Servo gaugeMotor; const int GAUGEMOTORPIN = 6; const unsigned long DISPUPDATETIME = 500; const unsigned long TIMEBETWEENREADS = 3000; unsigned long lastMove = millis(); unsigned long lastUpdate = millis(); void setup() { // Initialize the serial port Serial.begin(9600); // Initialize I2C Wire.begin(); // Initialize the servo motor gaugeMotor.attach(GAUGEMOTORPIN); // Initialize the magnetometer compass.init(); // Initialize the stepper motor stepper.setCurrentPosition(0); stepper.setMaxSpeed(500); // Initialize the LCD screen.init(); // turn on the backlight to start screen.backlight(); // set cursor to home position, i.e. the upper left corner screen.home(); } void loop() { // read ultrasonic ranger int currDist = sonar.ping_cm (); mVal2 = currDist; Serial.print("Ping: "); Serial.print(currDist); // Send ping, get distance in cm and print result (0 = outside set distance range) Serial.println("cm"); // map range distance to degree range 15 to 165 degrees int mappedAngle = map(currDist, 3, 11, 15, 165); oVal = mappedAngle; // write to LCD with appropriate timing if (millis() - lastUpdate >= DISPUPDATETIME){ screen.clear(); screen.print("i:"); screen.print(iVal); screen.setCursor(5, 0); screen.print("m:"); screen.print(mVal1); screen.setCursor(7, 1); screen.print(mVal2); screen.setCursor(11, 1); screen.print("o:"); screen.print(oVal); screen.home(); lastUpdate = millis(); } // write to servo with appropriate timing if (millis() - lastMove >= TIMEBETWEENREADS){ // move motor if (mappedAngle >= 15 && mappedAngle <= 165){ gaugeMotor.write(mappedAngle); lastMove = millis(); } } // read from magnetometer and write to stepper with appropriate timing if ((millis() - timer) >= wait) { if (stepper.distanceToGo() == 0) { Serial.println("in if statement"); int z; compass.read(); z = compass.getZ(); iVal = map(z, 0, 37000, 0, 99); Serial.println(z); int loc = map(z, 0, 37000, 0, -200); if (0 <= z && z <= 37000) { if (0 >= loc && loc > -50) { stepper.moveTo(0); } else if (-50 >= loc && loc > -100) { stepper.moveTo(-50); } else if (-100 >= loc && loc > -150) { stepper.moveTo(-100); } else if (-150 >= loc && loc >= -200) { stepper.moveTo(-150); } Serial.println(loc); stepper.setMaxSpeed(25); stepper.setAcceleration(25); timer = millis(); mVal1 = map(z, 0, 37000, 0, 99); } } } stepper.run(); }
Overall photo
Video of working machine: when rotational speed is at its highest, the propeller spins fast and presses down on the force sensor a lot, which translates to the maximum rotation of servo motors bringing the magnet all the way to the right edge of the board.
Detail photos of parts
Servo motors holding the magnet (our output)
Propeller connected to a DC motor, standing upright with force sensor at the bottom of the propeller arm (our middle step)
Noel’s Board
Video
The encoder constantly samples rotational speed and determines the voltage passed on to the motor. The greater force translates across the FSR and is translated into servo position. Jittery human input produces similarly jittery magnet position.
Detail photos of parts
Our solution to the servo-encoder interface between projects, featuring hot glue and luck
The DRV8833, the chip that allows us to drive the DC motor with more than on and off
Narrative Description:
Our input is rotational speed. That is measured by how fast the knob (rotary encoder) gets turned at the left of our boards. This rotational speed is then used to calculate how fast we turn our DC motor, which is connected to a propeller. The propeller sits upright in the middle of our board and it is built in a way so that depending on the spinning speed, the propeller arm tilts in differing amounts to press onto the square force sensor resistor at the bottom. Based on the force reading, degree of rotation for the servo motors are determined. The servos are holding a piece of magnet and greater rotation equals to further distance of the magnet to the next team’s board. With different distances, we create different strengths of magnetic field which is our output.
Progress Images:
An early mock up of the motor wiring. Using a motor with an encoder and voltage regulator board. Replaced by simple motor and motor driver.
Force Sensor Testing: wiring up the fsr to see how it reads by making it print in Serial Monitor Testing out the positions of servo motors. This was a critical step as we changed from our original design of using rubber bands arms sliding the magnet along a track to using a rigid arm.
One of the final troubleshooting phases. After re-seating every cable and testing every sensor, an incompatible power supply was the culprit.
Discussion:
The development of this project was a rollercoaster. Initial ambitions were blinding and made issues very easy to overlook, and there were several that we did. We made two simple backups and spent all of our creative power on integrating a windmill. We easily overlooked issues like creating linear motion with servos, balancing and connecting a propeller, and the interface between encoder and previous project’s servo. Our imagined outcome was not adherent to all the real physical limitations, and this would be apparent when we tried to share ideas. Solidworks, hand drawings, and real physical progress ultimately bridged the gap between the imagined and the real.
Through our build we were constantly reminded of the many tools that had already been developed before us. In an attempt to use as powerful a motor as we could find, we showed our lack of intuition for thrust and knowledge of available parts. Thanks to our professor, online resources, and physical testing, we derived something efficient and conceivable. These resources were invaluable, as neither of us had any idea a DRV8833 (dual motor driver carrier) was an available tool. Peers also helped ideation, and the learning environment was a significant help in understanding what we could accomplish. Miniature breadboards and switching to square FSRs were two results of the vibrant learning environment.
One of the most frustrating steps was connecting libraries and pre-existing code with our own. From completely unresponsive to mapped and tuned scaling, the troubleshooting often involved scrapping entire drafts and scouring for relevant sample code. Here it was obvious that we were “standing on the shoulders of giants.” With documentation from the course webpage and from online users with valuable experience with each part, we had plenty of comments to read and build our understanding of how we could connect each sensor and output device. Despite the plethora of technical documentation, we still sometimes found ourselves adjusting parameters through trial and error. In combining available resources and first hand experience, we were able to develop the system we imagined.
Functional Block Diagram:
Schematic:
Code:
/*Project Title: Double Transducer: Rotational Speed -> Magnetic Field Strength Team Members: Noel Barnes, Yuqi (Alice) Zhou Description: Arduino takes in input from a rotary encode to calculate the rotational speed. This speed is then mapped into a speed that's feed into the DC motor to drive a propeller. FSR then picks up a force reading from the tilt of the Propeller arm. The force reading is then mapped to a rotation degree for the servo motor. Servo motors are moved based on the rotation to move the magentic forward. Pin mapping: Arduino pin | role | description ------------|--------|------------- A0 input FSR for force sensing 2 input rotary encoder pin A 3 input rotary encoder pin B 5 input drv8833 A2 IN 6 input drv8833 A1 IN 10 output servo motor #1 11 output servo motor #2 SCL SCL of LCD SDA SDA of LCD Rotoray Encoder Code adapted from https://dronebotworkshop.com/rotary-encoders-arduino/#Arduino_Motor_Encoder_Sketch DroneBot Workshop 2019 DC motor code adapted from https://courses.ideate.cmu.edu/16-223/f2016/text/lib/WheelDrive.html#wheeldrive-doxygen */ #include <Servo.h> #include <Wire.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C screen(0x27, 16, 2); // Motor encoder output pulse per rotation (change as required) #define ENC_COUNT_REV 374 // Encoder output to Arduino Interrupt pin #define ENC_IN 3 // Pulse count from encoder volatile long encoderValue = 0; // interval for measurements of rotational speed int interval = 100; const int quaterSec = 250; unsigned long timer = 0; long previousMillis = 0; long currentMillis = 0; int rpm = 0; int rpmDisplay; // RPM display for the LCD screen int motorRPM; // DC motor speed int dcDisplay; // DC motor speed display for LCD screen //make a servo object Servo lilMotor; Servo secMotor; const int LILMOTORPIN = 11; // Servo Motor to pin 11 const int SECMOTORPIN = 10; // Servo Motor to pin 10 int force; // the analog reading from the FSR int forceDisplay; // force display for the LDC screen int fsrPin = 0; // the FSR and 10K pulldown are connected to a0 int rotation; // rotation degree for lilMotor int rotationTwo; // rotation degree for secMotor int rotationDisplay; // servo motor rotation display for LDC screen #define A1PIN 6 #define A2PIN 5 void setup() { // Set encoder as input with internal pullup pinMode(ENC_IN, INPUT_PULLUP); // Attach interrupt attachInterrupt(digitalPinToInterrupt(ENC_IN), updateEncoder, RISING); // Setup initial values for timer previousMillis = millis(); // initialize DC motor pinMode(A1PIN, OUTPUT); pinMode(A2PIN, OUTPUT); digitalWrite(A1PIN, LOW); digitalWrite(A2PIN, LOW); // tell Arduino where the servo motors are plugged in lilMotor.attach(LILMOTORPIN); secMotor.attach(SECMOTORPIN); // initialize the screen (only need to do this once) screen.init(); // turn on the backlight to start screen.backlight(); // lcd display template screen.home(); screen.print("i:"); screen.setCursor(6, 0); screen.print("m:"); screen.setCursor(12, 1); screen.print("o:"); } void loop() { // Update RPM value currentMillis = millis(); if (currentMillis - previousMillis > interval) { previousMillis = currentMillis; // Calculate RPM rpm = (float)(encoderValue * 60 / ENC_COUNT_REV); //follow the range of 20-60 for rotational speed if (rpm > 60) { rpm = 60; } if (rpm < 20) { rpm = 20; } encoderValue = 0; } //rotational speed display if (millis() - timer >= quaterSec) { screen.setCursor(2, 0); rpmDisplay = map(rpm, 20, 60, 0, 99); screen.print(rpmDisplay); } // run DC motor with RPM motorRPM = map(rpm, 20 , 60, 80, 255); //normalize rotational speed to input as DC motor speed digitalWrite(A1PIN, LOW); analogWrite(A2PIN, motorRPM); delay(2); // DC motor speed display if (millis() - timer >= quaterSec) { screen.setCursor(8, 0); dcDisplay = map(motorRPM, 80, 255, 0, 99); // normalized for display screen.print(dcDisplay); } // force reading force = analogRead(fsrPin); // limit to a force range that's appropriate for the propeller arm setup if (force > 400) { force = 400; } if (force < 50) { force = 50; } // force sensor display if (millis() - timer >= quaterSec) { forceDisplay = map(force, 50, 400, 0, 99); // normalized force for display screen.setCursor(8, 1); screen.print(forceDisplay); } // calculate rotation for the two servo motors rotation = map(force, 50, 400, 10, 170); // map force reading to motor rotation degree rotationTwo = 180 - rotation; // servo motor rotation degree display if (millis() - timer >= quaterSec) { rotationDisplay = map(rotation, 10, 170, 0, 99); // normalized rotation degree for display screen.setCursor(14, 1); screen.print(rotationDisplay); timer = millis(); } // rotate the servo motors lilMotor.write(rotation); secMotor.write(rotationTwo); } void updateEncoder() { // Increment value for each pulse from encoder encoderValue++; }
]]>
This project is a double transducer that takes a rotational position (0-90) as input and produces a frequency of tapping (20-60 taps per min) as output. The double transducer takes in the rotational degree and adjusts the rotation of the stepper motor accordingly. The stepper motor then drives the pulley to change the position of the obstacle. The ultrasonic ranger detects the distance between the obstacle and itself to adjust the frequency of tapping in accordance.
Overall Logic:
Rotational Position (Potentiometer) –> Stepper Motor Rotation –> Pulley System Movement –> Obstacle Movement –> Distance Detection by Ultrasonic Ranger –> Frequency of Tapping (Solenoid)
Overall Photos
Serena’s Overall
Bella’s Overall
Detailed Photos
Stepper Motor
Stepper Motor Pulley System
Stepper Motor Driver Closeup
Solenoid Driver Circuit
Stepper Motor (left) and Solenoid (right) Driver Circuits
Movies
Serena’s video:
Bella’s video:
Progress Images
Figuring out the solenoid voltage problem
Figuring out how to use a solenoid
Added LCD display and solenoid to the mix after switching to a stepper motor for a more hefty rotational force
Discussion
The project taught us that implementing a fun idea could present so many challenges, ones that we anticipate and ones that come out of nowhere. For this project, some parts we were prepared to tackle with patience from the beginning, such as the pulley system controlled by the stepper motor. We spent a long time brainstorming on how to use a stepper motor, how to make the pulley work, how to move the obstacle attached to the pulley, etc. But the real frustration came from the parts that we thought would be as easy as a breeze, such as a solenoid. The solenoid seemed to be simpler than most of the things we’ve seen in class. However, it was unexpected that adding the solenoid would cause a disruption for the voltage for the entire project. The solenoid causes the 5V to drop to around 3V whenever it is activated, causing the LCD display to dim and the stepper motor to shake. After an hour of tinkering in the OH on the night before the due, we still could not find the problem. Since we left the solenoid to be the last to do, it was too late to find an alternative to solve the issue. This experience shows that every part of the project should be tested out in the beginning stage, even the ones that appear to be easy to implement.
This project also showed the importance and efficiency of the “divide and conquer” strategy. During the early stages, we experimented with different sections of the project. While one of us attempts to figure out how to use a stepper motor, the other one would fiddle with the LCD display. Being responsible for different tasks and piecing them together in the end allowed us to work efficiently and effectively within a limited time.
Functional Block Diagram
Schematic Diagram
Code
/* * Double Transducer: Rotational Position -> Frequency of Tapping * * Team Members: Bella Liu, Serena Ying * * Description: * The program is designed to receive input from the potentiometer and convert * the value to appropriate rotatoin for the stepper motor in order to control * the pulley and therefore move the obstacle. The ultrasonic ranger then detects * the distance between itself and the obstacle, and converts it to a frequency for * the solenoid tapping accordingly. * * Pin Mapping Table: * * Arduino pin | description * ------------|------------- * A0 Potentiometer * * 11 Echo pin, ultrasonic ranger * 12 Trigger pin, ultrasonic ranger * * 2 Step pin, A4988 driver chip for stepper motor * 3 Direction pin, A4988 driver chip for stepper motor * * SDA SDA pin, LCD display * SCL SCL pin, LCD display * * * References: * https://courses.ideate.cmu.edu/60-223/f2021/tutorials/ultrasonic-ranger * https://courses.ideate.cmu.edu/60-223/f2021/tutorials/stepper * https://courses.ideate.cmu.edu/60-223/f2021/tutorials/I2C-lcd */ // Libraries #include <Wire.h> #include <LiquidCrystal_I2C.h> #include <AccelStepper.h> #include <NewPing.h> //Pin for potentiometer const int POTEN = A0; //Pins for ultrasonic ranger const int TRIGGER_PIN = 12; const int ECHO_PIN = 11; const int MAX_DISTANCE = 200; //Pins for A4988 diver chip 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) //Variables for LCD display unsigned long LCDtimer = 0; const int LCDwait = 500; bool LCDstate = LOW; //Pin and variables for solenoid const int SOLENOID = 5;//This is the output pin on the Arduino unsigned long SOLEtimer = 0; int SOLEwait = 0; bool SOLEstate = LOW; //Setups NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); LiquidCrystal_I2C screen(0x27, 16, 2); AccelStepper motor(1, STEP_PIN, DIR_PIN); void setup() { //Pin setup pinMode(POTEN, INPUT); pinMode(SOLENOID, OUTPUT); Serial.begin(9600); Serial.begin(115200); //LCD screen initialization screen.init(); screen.backlight(); // set cursor to home position, i.e. the upper left corner screen.home(); //Stepper motor initialization motor.setMaxSpeed(6000); // measured in steps per second motor.setAcceleration(8000); // measured in steps per second squared } void loop() { //Variables to store values for potentiometer and ultrasonic ranger int potVal = analogRead(A0); int ranger = sonar.ping_cm(); //Printing potentiometer and ultrasonic ranger values in the monitor for debugging Serial.println(potVal); Serial.print("ping:"); Serial.print(sonar.ping_cm()); Serial.println("cm"); //Adjust the stepper motor rotation according to the rotational degree int motorPos = map(potVal, 0, 1023, 0, 400); motor.moveTo(motorPos); motor.run(); //Adjust the frequency of tapping based on the distance detected by the ranger SOLEwait = map(ranger, 19, 0, 800, 300); if ( (millis() - SOLEtimer) >= SOLEwait ) { SOLEstate = !SOLEstate; SOLEtimer = millis(); } digitalWrite(SOLENOID, SOLEstate); //display on the LCD if ( (millis() - LCDtimer) >= LCDwait ) { // millis() is always the number of milliseconds since the Arduino powered up LCDstate = !LCDstate; // this will "toggle" the value of "quarterStateLight" to its opposite LCDtimer = millis(); // reset the timer before exiting the function. } //display values for the LCD int input = map(potVal, 0, 1023, 0, 99); int mid = map(motorPos, 0, 400, 0, 99); int mid2 = map(ranger, 20, 0, 0, 99); int output = map(SOLEwait, 300, 100, 0, 99); if (LCDstate == HIGH) { screen.clear(); screen.print("i:"); screen.setCursor(6,0); screen.print("m:"); screen.setCursor(6,1); screen.print("m:"); screen.setCursor(12,1); screen.print("o:"); screen.setCursor(2, 0); screen.print(input); screen.setCursor(8, 0); screen.print(mid); screen.setCursor(8, 1); screen.print(mid); screen.setCursor(14,1); screen.print(output); } }
]]>
Our double transducer took color as an input, transforming it into rotational position.
The final double transducer, copy 1.
The second copy of the double transducer we made.
The first part is the color sensor (at the top of the images), which reads what color is put in front of it. After turning it on, we make sure the color sensor is at the correct position, so we have to put a color in front of it and press the button, which makes 1/10th of a full rotation, until the color sensor is pointing to the correct location on the color wheel. When a different color is put in front of the color sensor, the color sensor reads the color and spins the motor to point to the color we are reading. The position of the motor puts pressure on a different sensor. Depending on what location the second sensor is being pressed, the final motor turns a certain amount.
Detail shot: the small pillars we added in order to stabilize the potentiometer input.
Detail shot: the arm attached to the stepper motor which puts pressure on the potentiometer.
Detail shot: the soldered board.
Detail shot: The second color sensor, with a busted LED that we had to replace.
Our day 1 design for the potentiometer. Unstable, ultimately redone.
First attempt at getting the stepper motor to work
First working color sensor to stepper motor rotation to potentiometer read
Finished the soldering on the first board, and verified that the motor works.
The first day we spent the entire two hours of class trying to work with the force sensing circular potentiometer, trying to get a consistent reading on it. We fiddled with it for ages, trying to find something that could move and apply enough pressure to the potentiometer for it to consistently register. Our first attempt at this was to add weights to the pointer part of the arm that would apply pressure. However, by the end of it, it still wasn’t working, and we had to consider our options. We came up with a few: first, we could continue working with the potentiometer, with our next steps being that we would move it to a more stable surface; and two, we could create what was basically our own potentiometer by moving a stepper motor with resistors surrounding it and measuring the change in voltage. However we did end up making the circular potentiometer work by creating a more stable surface, and adding support beams and weights to the arm. This was definitely the hardest part of the project.
Something that initially seemed fairly daunting, was soldering. Neither of us had much experience with soldering in the past, and the number of pins that needed to be soldered to run the stepper motor was very scary. However, after the first 10-15 pins, the we picked up a feel for how to work the soldering iron and make pretty solders. The second board soldering process went by much faster, and overall would say that soldering feels fairly easy now especially considering where we started.
One decision that would have changed everything about what we learned and how cool our project ended up being, is using the easy middle step that we had initially discussed. One of our three initial ideas was to use the brightness of a bulb to change a photoresistor resistance and measure that change. We would have had a much easier soldering step, which means we would likely not have been as comfortable as we are now with it, and it also would have removed the entire centerpiece of our final design, which was the color wheel to stepper motor interaction that lets us display color.
A video demonstration of the color wheel working. There is a slight delay between the color input and the movement of the motors; this is due to the calculations made to get the raw data from the sensors.
/* Double Transducer: Color to Rotational Position 1. Reads values from the color sensor, 2. Converts it into a degree value based on the color sensed from the color sensor 3. Moves stepper motor stepwise until it reaches the number of degrees we got from the color sensor 4. Reads potentiometer reading 5. Translate potentiometer reading into an amount to move the final servo motor Credit: https://www.py4u.net/discuss/63122 for the code to convert rgb values to hsv values */ #include <AccelStepper.h> #include <Servo.h> #include <Wire.h> #include "Adafruit_TCS34725.h" #include <LiquidCrystal_I2C.h> const int STEP_PIN = 12; // A4988 "STEP" pin wired to Arduino pin 2 for stepper const int DIR_PIN = 13; // A4988 "DIRECTION" pin wired to Arduino pin 3 for stepper motor const int buttonPin = 2; // pin for button press const int servoPin = 3; // pin for controlling servo motor Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_614MS, TCS34725_GAIN_1X); LiquidCrystal_I2C lcd(0x27, 16, 2); // initializes typedef struct { double h; // angle in degrees double s; // a fraction between 0 and 1 double v; // a fraction between 0 and 1 } hsv; static hsv rgb2hsv(float r, float g, float b); // converts rgb values to hsv values hsv rgb2hsv(float r, float g, float b) { hsv out; double min, max, delta; min = r < g ? r : g; min = min < b ? min : b; max = r > g ? r : g; max = max > b ? max : b; out.v = max; // v delta = max - min; if (delta < 0.00001) { out.s = 0; out.h = 0; // undefined, maybe nan? return out; } if( max > 0.0 ) { // NOTE: if Max is == 0, this divide would cause a crash out.s = (delta / max); // s } else { // if max is 0, then r = g = b = 0 // s = 0, h is undefined out.s = 0.0; out.h = NAN; // its now undefined return out; } if( r >= max ) // > is bogus, just keeps compilor happy out.h = ( g - b ) / delta; // between yellow & magenta else if( g >= max ) out.h = 2.0 + ( b - r ) / delta; // between cyan & yellow else out.h = 4.0 + ( r - g ) / delta; // between magenta & cyan out.h *= 60.0; // degrees if( out.h < 0.0 ) out.h += 360.0; return out; } Servo serv; // make an AccelStepper motor object. AccelStepper myMotor(1, STEP_PIN, DIR_PIN); long long count = 0; long total = 0; long sampleSize = 0; int calibPos = 0; int pos = 0; long long setPosTimer = 0; long long buttonTimer = 0; long long int degree = 0; char color[20]; void setup(){ Serial.begin(9600); // begin Serial communication pinMode(buttonPin, INPUT); //checks color sensor is working Serial.println("setupr"); if (tcs.begin()) { Serial.println("Found sensor"); } else { Serial.println("No TCS34725 found ... check your connections"); while (1); } // starts the display lcd.begin(); lcd.backlight(); lcd.print("Hi!"); // you can change the below values as you'd like myMotor.setMaxSpeed(150); // measured in steps per second myMotor.setAcceleration(50); // measured in steps per second squared serv.attach(servoPin); setPosTimer = millis(); myMotor.moveTo(0); while(myMotor.distanceToGo() != 0) { myMotor.run(); } } void loop(){ // calculates color input if (millis() - setPosTimer >= 5000 && millis() >= 10000) { uint16_t r, g, b, c; tcs.getRawData(&r, &g, &b, &c); float red, green, blue; tcs.getRGB(&red, &green, &blue); red = red / 255; green = green / 255; blue = blue / 255; double h = rgb2hsv(red, green, blue).h; int hue = int(h); Serial.println(hue); if (hue >= 0 && hue < 15) { strncpy(color, "red", 3); } else if (hue >= 15 && hue < 45){ strncpy(color, "orange", 6); } else if (hue >= 45 && hue < 75){ strncpy(color, "yellow", 6); } else if (hue >= 75 && hue < 150){ strncpy(color, "green", 5); } else if (hue >= 150 && hue < 200){ strncpy(color, "cyan", 4); } else if (hue >= 200 && hue < 255){ strncpy(color, "blue", 4); } else if (hue >= 255 && hue < 285){ strncpy(color, "purple", 6); } else if (hue >= 285 && hue < 315){ strncpy(color, "pink", 4); } else if (hue >= 315 && hue < 360){ strncpy(color, "red", 3); } else { strncpy(color, "N/A", 3); } lcd.home(); lcd.print("Color: "); lcd.print(color); strncpy(color, " ", 10); // moves stepper motor int stepperTarget = map(hue, 0, 360, 0, 200) + calibPos; Serial.print("Current pos and Target pos: "); Serial.print(pos); Serial.print(", "); Serial.println(stepperTarget); myMotor.moveTo(stepperTarget); pos = stepperTarget; setPosTimer = millis(); lcd.setCursor(0, 1); lcd.print("moving to: "); lcd.print(stepperTarget); } // Use button to calibrate without changing pos if (digitalRead(buttonPin) == HIGH && millis() - buttonTimer >= 1000){ Serial.println("Press"); calibPos += 20; myMotor.moveTo(pos); buttonTimer = millis(); } // moves servo motor if ( count % 100 == 0 ) { int servPos = map(total/100, 0, 1023, 10, 90); Serial.println(servPos); serv.write(servPos); total = 0; } total += analogRead(A0); count++; myMotor.run(); }
]]>
A view of the arrangement of all parts positioned on the 12″ by 12″ board with the switch input on the left and the speaker on the right.
A detailed view of the belt pulley system with the tape board for distance, a stepper motor and corresponding post, and the infrared proximity sensor on top of the block.
A view of the speaker used.
A view of the soldering connections made with the A4988 microstepping motor driver.
A button is on the left edge of the board. In the center, there is a motor, a rubber band with a piece of paper attached to it, and a sensor. On the right edge of the board, there is a speaker constantly playing a sound. The faster the button gets pressed, the more the motor moves the piece of paper on the rubber band toward the sensor. When the paper gets closer to the sensor, the speaker plays a higher-pitched sound.
At this stage, we were considering using a smaller stepper motor and attaching a 3D printed part to the shaft. However, the code we ran only worked on the bigger motor and the 3D print was difficult to attach, so we decided to go with the larger motor and find parts that are already in the shop.
At this stage in the process, the proximity sensor was working and the lever switch was registering the amount of presses in each minute. While each part worked on its own, the parts did not communicate with one another and the refresh rate of the taps was not fast enough to be effective.
After the stepper motor was in place, it was a bit of a challenge to figure out how to get the mechanism to run smoothly and reliably. Emily had made wooden posts to hold the opposite side, but it took a lot of experimenting with various ball bearings and posts to rotate smoothly while maintaining proper tension. A thin rubber band was also key for not being so strong that it moves the motor or the post.
At this time we were considering changing our design to a change in angle rather than distance as the professor recommended since it would not rely on the mechanics as much. While the deadline was approaching and we still hadn’t actually tested the rubber band belt, thankfully we got the system to work and we decided to go through with our original plan.
Although it was frustrating, irksome, and disappointing at times, we’re genuinely grateful for the experience as a whole. When we started, we thought it would be easy to execute our vision since we knew exactly what we wanted. We had everything planned out, so assembling the project should have been fairly straightforward; they weren’t though. Due to our lack of knowledge, experience, and skill, we ran into a lot of unforeseen problems that stumped us for hours at times! An example is when David spent hours revising his code and looking over his wiring to get the stepper motor to work only to have the professor point out that the issue was with the external power supply. There are plenty of examples of simple things holding our progress back, but to name them all would require a book. It’s because of these experiences, however, that we can look back and be thankful; now all of our sorrows become our teachers. We won’t forget semicolons or mix up the wiring for infrared sensors in the future because we’ve already dealt with those mistakes in the past. I think that the lessons we learned will enable us to do a lot more in the upcoming projects. Since our foundation is stronger, we’ll have more creative liberty that will enable us to experiment with things that require more advanced techniques. Overall, the project was a profound learning experience.
One thing we’d do differently in the future is allocate more time toward physically assembling our project. We spent a lot of time getting our individual parts to work (partly because we had to learn how to use them), so we didn’t have much time for integrating everything together. This led to assembly feeling rushed, and some parts looking a bit messy. For example, the paper that is attached to the belt will often fall off if it isn’t handled delicately. If we had made more time to experiment with different mediums and structures, we could have found out what would have worked best for our project, and the assembly process would have been a lot smoother.
Overall, we’re happy with what we’ve accomplished. There are no huge design changes we would make because we got to develop an idea we had from the start and see it through to the end. We’re satisfied because we chose to create something that posed a challenge, but it wasn’t so unfeasible that we had to revert to something less complicated. We executed our idea and made things work, and that feels good.
/* * A double transducer: rate of tapping to position to pitch of beeping * * Description: This code measures rate of tapping by evaluating the * length of time between taps and translates it to a position value * on a stepper motor. As the stepper motor moves an index card, an * infrared proximity sensor measures the distance of the card and * sends it back to the Arduino where that value is then mapped to * a value of pitch sent to a speaker. * * Inputs: Lever switch, Infrared proximity sensor * Arduino pin | input * 8 Lever switch * A0 IR Proximity Sensor * * Outputs: Stepper motor direction, stepper motor steps, speaker * Arduino pin | output * 3 Stepper Motor Steps * 4 Stepper Motor Direction * 10 Speaker * */ // Additional help and code adapted from: // https://courses.ideate.cmu.edu/60-223/f2021/tutorials/I2C-lcd // https://courses.ideate.cmu.edu/60-223/f2021/tutorials/IR-proximity-sensor // https://courses.ideate.cmu.edu/60-223/f2021/tutorials/button-and-switch // https://courses.ideate.cmu.edu/60-223/f2021/tutorials/stepper //INITIALIZATION #include <Wire.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C screen(0x27, 16, 2); //Pin names const int CLICKERPIN = 8; //Pin used to record clicker input from tapping const int STEP_PIN = 3; //Pin used to tell stepper motor how much to move const int DIR_PIN = 4; //Pin used to tell stepper motor the direction const int SPEAKER_PIN = 10; //Pin used to tell speaker the pitch const unsigned long IRPIN = A0; // shortcut to refer to the ir proximeter pin later unsigned long tap1 = 0; //Records time button was pressed unsigned long tap2 = 0; //Records last time button was pressed #include <AccelStepper.h> AccelStepper myMotor(1, STEP_PIN, DIR_PIN); int motorPos = 0; int tapRate = 20; //How many taps per minute unsigned long timerOne = 0; //Timer for screen refresh rate int buttonState = 0; //VOID SETUP void setup() { //Data setup Serial.begin(9600); //Screen setup screen.init(); screen.backlight(); myMotor.setMaxSpeed(1000); myMotor.setAcceleration(500); //Pin Inputs/Outputs pinMode(IRPIN, INPUT); // we will be reading the photoresistor pin as an input pinMode(CLICKERPIN, INPUT); pinMode(SPEAKER_PIN, OUTPUT); } //VOID LOOP void loop() { int readVal; // initialize a new integer to store the IR value readVal = analogRead(IRPIN); // do the analog read and store the value int pitchVal; // initialize a new integer to store the IR value pitchVal = map(readVal, 15, 60, 200, 2000); //Mapping the IR distance values to pitch values tone (SPEAKER_PIN, pitchVal); //Telling speaker to play a tone at pitch pitchVal //Beginning conditional for when neither tap has a value if ((digitalRead(CLICKERPIN) == 1) and (tap1 == 0)) { tap1 = millis(); tapRate = (60 / (tap1 * .001)); if (tap1 <= 1000) { tapRate = 60; //Covers boundary conditions, no higher than 60 } if (tap1 >= 3000) { tapRate = 20; //Covers boundary conditions, no lower than 20 } } if ((digitalRead(CLICKERPIN) == 1) and ((millis() - tap1) > 1000)) { tap2 = millis(); Serial.println((String)"tap1:" + (tap1 * .001) + "s"); Serial.println((String)"tap2:" + (tap2 * .001) + "s"); tapRate = (60 / ((tap2 - tap1) * .001)); if ((tap2 - tap1) <= 1000) { tapRate = 60; //Covers boundary conditions, no higher than 60 } if ((tap2 - tap1) >= 3000) { tapRate = 20; //Covers boundary conditions, no lower than 20 } motorPos = map(tapRate, 20, 60, 0, 200); //Mapping the tapRate values to position values myMotor.moveTo(motorPos); Serial.println(motorPos); tap1 = tap2; } myMotor.run(); if (millis() - timerOne >= 300) { //Refresh rate of screen every 300 milliseconds // print the input value onto the screen starting at the above cursor position screen.clear(); screen.home(); screen.print("i:"); screen.print(tapRate); screen.setCursor(5, 0); screen.print("m:"); screen.print(motorPos); screen.setCursor(7, 1); screen.print(readVal); timerOne = millis(); screen.setCursor(10, 1); screen.print("o:"); screen.print(pitchVal); } }
]]>
Close-up image of the three-axis accelerometer attached to the servo motor.
Close-up image of photoresistor and voltage divider circuit.
Close-up image of power rail.
Close-up image of our output, a continuous servo motor that rotates continuously with the speed of around 20 – 60 rpm.
Description
The photoresistor reads light brightness and sends the information to the servo motor which rotates in both directions. The motor turns based on the brightness of light and tilts the three-axis accelerometer that is attached to the servo motor. The accelerometer measures the acceleration in the axes X, Y, and Z and sends information to the continuous motor that rotates continuously in one direction. Based on the tilt of the three-axis accelerometer, the speed of the continuous servo motor changes.
Progress Images
I made this wooden holder to keep the accelerometer in place. We determined that the long stick part of the holder was unnecessary so we decided to cut that part off.
The first attempt at making something that would hold the accelerometer was a pipe cleaner.
We also tried using a bit of styrofoam to hold the accelerometer.
I soldered wires to connect the three-axis accelerometer to the protoboard. I used solid wires at first however it was too stiff causing the movement of the accelerometer to be inflexible as shown above in the image. I later soldered them again using stranded wires.
Discussion
Through this project we learned a lot especially from the challenges we faced and the process of problem solving. One of the challenges we faced was reading the changes in tilt of the three-axis accelerometer. We were reading the acceleration values of all three axes, X, Y, and Z, through the serial monitor as we were tilting the accelerometer, however the changes of the values were so subtle it was difficult to tell which axis we were dealing with. We took the norm of all three values to get one total accelerometer reading using this equation: finalReading = sqrt(X^2 + Y^2 + Z^2). This reading has much more variance than each individual sensor reading.
Another challenge that I faced had to do with soldering. I decided to solder wires connecting the accelerometer to the protoboard and I used solid wires which are pretty stiff making the movement of the accelerometer inflexible. I had to solder them again, but this time I used stranded wires, which are a lot more flexible, easing the movement of the accelerometer.
Through this project, we also learned about a type of motor that we used as our output. Our original plan was to use a stepper motor to produce a continuous rotation however we struggled to figure that out because it required a transistor. However, we came up with an alternative solution. We were introduced to a different type of motor that we could use as our output instead of the stepper motor, which is a continuous servo motor. Unlike a hobby servo motor which rotates only up to 180 degrees, the continuous servo motor rotates 360 degrees continuously, allowing us to produce our output of a continuous rotation of around 20 to 60 rpm. Learning about this new part allowed us to create our output effectively and efficiently.
Functional Block Diagram
Schematic
Code
/* * Double Transducer: Light Brightness to Rotational Speed * * Team: Ronald Gonzalez and Sohye Park * * Description: * The code reads the light input from the photoresistor * going to pin A0 and then sends the signal generated * to the half rotation servo on pin 3 which will rotate * an accelerometer which sends all 3 of it's signals to * pins A1-3 then the norm of those 3 values is taken and * that is sent to the full rotation servo whose speed * will change based on the signal sent. * * Arduino Pin | Description * ------------|--------------------- * A0 | PhotoResistor * A1 | Accelerometer x * A2 | Accelerometer y * A3 | Accelerometer z * 3 | Half Rotation Servo * 5 | Full Rotation Servo * * Code for Servo from Michael Margolls * Code for LED screen from Frank de Brabander */ // Libraries #include <Servo.h> #include <Wire.h> #include <LiquidCrystal_I2C.h> // Pins #define PHOTORES A0 #define ACCELX A1 #define ACCELY A2 #define ACCELZ A3 #define HALFROTSERVO 3 #define FULLROTSERVO 5 // Servos and Screen Servo gaugeMotor1; Servo gaugeMotor2; LiquidCrystal_I2C screen(0x27, 16, 2); // Timer unsigned long lastPrint = 0; void setup() { // Pin setup pinMode(PHOTORES, INPUT); pinMode(ACCELX, INPUT); pinMode(ACCELY, INPUT); pinMode(ACCELZ, INPUT); // Motor setup gaugeMotor1.attach(HALFROTSERVO); gaugeMotor2.attach(FULLROTSERVO); // Screen setup screen.init(); screen.backlight(); screen.home(); } void loop() { // Analog reads from photoresistor and accelerometer int light = analogRead(A0); long accelx = analogRead(A1); long accely = analogRead(A2); long accelz = analogRead(A3); // Taking the norm of all three accelerometer outputs long accelFinal = long(sqrt(((accelx * accelx) + (accely * accely) + (accelz * accelz)))); // Mapping all values to appropriate ranges int angle = map(light, 50, 1050, 10, 180); int rpm = map(accelFinal, 500, 630, 93, 105); int sLight = map(light, 50, 1050, 0, 99); int sAngle = map(angle, 10, 180, 0, 99); int sRPM = map(rpm, 93, 105, 0, 99); int sAccel = map(accelFinal, 500, 620, 0, 99); // Sending correct angle and rpm to the two servo motors gaugeMotor1.write(angle); gaugeMotor2.write(rpm); // Delay so both motors aren't being updated at extremely // fast rates delay(50); //Updates screen every 250 milliseconds if (millis() - lastPrint >= 250) { screen.clear(); screen.setCursor(0, 0); screen.print("i:"); screen.setCursor(6, 0); screen.print("m:"); screen.setCursor(12, 1); screen.print("o:"); screen.setCursor(2, 0); if (sLight < 10){ screen.print("0"); } screen.print(sLight); screen.setCursor(8, 0); if (sAngle < 10){ screen.print("0"); } screen.print(sAngle); screen.setCursor(8, 1); if (sAccel < 10){ screen.print("0"); } screen.print(sAccel); screen.setCursor(14, 1); if (sRPM < 10){ screen.print("0"); } screen.print(sRPM); lastPrint = millis(); } }
]]>