At last, a way to produce electronic music with physical gestures. Rotate your hand and wiggle a joystick to manipulate sound in four distinct ways.
At last, a way to produce electronic music with physical gestures. Rotate your hand and wiggle a joystick to manipulate sound in four distinct ways.
Relative proportion of Gesture Controller
Relative proportion of Joystick Controller
Graphical user interface and midi translator between Arduino and Logic Pro
The Controller was going to have two modes: Performance Mode and Wand Mode
In Performance Mode, all 3 axes of the accelerometer would control 3 different parameters. Side to side for Pitch Bend and vibrato, front and back for Modulation, and up and down for Expression.
In Wand Mode, a single parameter would react to the overall motion.
In both modes, the joystick would control two parameters with the X and Y axes, but they would control slightly different parameters depending on the mode.
I had two major problems. In the first one, I had to calculate the velocity and position based on the acceleration readings, for all 3 axes.
Code from the old physics suite: getDeltTime(), noiseGated(), and getRestingState()
The problem is that accelerometer readings are noisy and give data even when not moving. This random data was accumulating in the velocity calculations and causing my variables to overflow, and thus the position calculations were even worse.
I tried noise gating the data when the accelerometer was not moving (i.e. when the readings were at 0), and only letting the data through when above a certain threshold. But above that threshold, the readings were still noisy and inaccurate.
I scrapped almost all of that code.
Demonstrating the new gestures the controller will respond to. (Far left) Printing accelerometer rotation readings.
Instead, I decided to make only one Mode, one that uses only the acceleration readings form the Y and Z axes. For this purpose, the acceleration readings were accurate enough, and I already had plenty of success using it.
The second problem was this. In both of the old modes and the new one, the joystick’s click function was going to turn Arduino’s output on and off. This would have let me choose when my motion was intended for performance and when to have the computer ignore it.
(Left) Joystick detects several clicks at a time (bouncing). (Right) Solution: apply a minimum delay that I measured with a stopwatch.
First, I the joystick was reading several button-downs and button-ups with one single click (called bouncing). My solution was for the Arduino, as soon as it read a button press, to wait 5 milliseconds before it read anything else. I measured the fastest I could possibly double-click button by using my watch’s stopwatch, and found that was around 8 milliseconds.
Successful debounce and communication with Max.
Once I successfully debounced the button, I also successfully made Max register the button-down as distinct from the button-up. I even got Max to freeze and unfreeze the joystick’s output based on if the button was pressed not.
Unfortunately, for some reason, putting the joystick and accelerometer together, I was only able to unfreeze the Arduino’s output with a click-down command, but never freeze it again with a click-up. I never knew why, and scrapped that feature, too.
This was one student’s response to my project, in particular, to my presentation.
It would be good to get a background about the project before diving right into the demo because I wasn’t sure what we were looking at initially.
I couldn’t agree more with this. The demonstration was just Logic playing back a synthesize chord, and my motion changing the sound in bizarre and uninteresting ways. I do like giving the audience a sense of discovery, but since I didn’t verbally give context, and my performance didn’t fill that gap either, my cues didn’t help the audience grasp the device’s usefulness.
Later I was playing around with my device, improvising a performance, and realized how easy and compelling it would’ve been to do that performance for my presentation. Rip.
I absolutely love this:
Is there a way to preset chords such that as you rotate your wrist, it plays a simple song forwards or backwards?
What comes to my mind is called an arpeggiator. This is a really good idea, and possibly an easy one to implement. I could do it in Max and have it generate MIDI notes there. Logic also has MIDI plugin that lets you program in JavaScript any MIDI effect; maybe I could do it there.
This critique was my favorite.
Maybe have some sort of way to set it while inside the bathroom and display outside
Go ahead and let that simmer :=)
My absolute greatest success in this project was making the simplest tests I could come up with. Even before I typed a single line of code, I chopped up the entire project into these tests, and scheduled out when I was going to make them, weeks in advance. This also included making schematics for each sub-circuit I had to test.
I ran tests as simple as printing the accelerometer axes on the Arduino. I made the Arduino, by itself, talk to Max with hard-coded numbers. I made Max, by itself, talk to Logic. Then I put all of those together and made the accelerometer talk to Max, and later, to Logic. And thus I scaffolded from very simple examples like that to the entire finished build.
This made it extremely easy to isolate the unique bugs of each subsystem and not let issues from other systems bleed over. Not only did I learn how to solve these issues, I mostly only had to solve them once! Because then I could take that success with me for the entire rest of the build!
I did find that each test was way less trivial than I thought at first. I would have way more problems than I expected, and the seemingly really long time cushions I would allocate for myself, I would spend all of it, or more, trying to solve those problems. It made me all the more grateful to have made “small” tests, and also to have scheduled such long cushions.
I will definitely keep these very good project management habits.
In the next iteration, I will definitely keep in mind that fantastic idea of arpeggiation. Maybe the angle of my wrist could correspond to playback speed, or I could build upon that idea in different ways.
I will also make a much sturdier, reusable, plug-and-play version setup. One that I don’t have to rip everything up every time I need the parts for something else. One that lets me just plug in the Arduino, the joystick, the accelerometer, and all of the cables.
Dissembling (actually destroying) the device
It would also be fantastic to have a drop-down menu in Max that lets me select what MIDI parameter I want each accelerometer and joystick axis to affect, and through what MIDI channel. Right now those things are hard-coded, and I need to unlock the patch and change the code to change the mapping.
Functional Block Diagrams and schematics for final build, as well as sub-systems
/* Gestural Midi Controller * * Carlos Ortega (carlosor) * * Reads data from all axes of an accelerometer and a joystick, * converts the data into a usable range for MIDI, * formats the data to be interpretted in Max/MSP, * and sends to Max. * * Pin Mapping: * * Accel x A0 * Accel y A1 * Accel z A2 * * Joy x A3 * Joy y A4 * * Joy Switch 2 * */ typedef int pin_t; // Accelerometer pin mappings pin_t ACCEL_X_PIN = A0; pin_t ACCEL_Y_PIN = A1; pin_t ACCEL_Z_PIN = A2; // Joystick pin mappings pin_t JOY_X_PIN = A3; pin_t JOY_Y_PIN = A4; pin_t JOY_SW_PIN = 2; // Axis indices typedef byte axis_t; axis_t AXIS_X = 0; axis_t AXIS_Y = 1; axis_t AXIS_Z = 2; axis_t JOY_SW = 255; // Device type typedef String device_t; device_t DEV_ACCEL = "accel"; device_t DEV_JOY = "joy"; typedef String clickstate_t; clickstate_t CLK = "clk"; clickstate_t UNCLK = "unclk"; clickstate_t NO_CHANGE = "no_change"; // Used for checking the moment // when the joystick switch was clickedf clickstate_t last_clck_state = UNCLK; unsigned long clickTimer = 0; // Returns pin number for device at axis pin_t getAxisPin(axis_t axis, device_t device) { pin_t pin = 0; if (device.equals(DEV_ACCEL)) { if (axis == AXIS_X) pin = ACCEL_X_PIN; else if (axis == AXIS_Y) pin = ACCEL_Y_PIN; else if (axis == AXIS_Z) pin = ACCEL_Z_PIN; } else if (device.equals(DEV_JOY)) { if (axis == AXIS_X) pin = JOY_X_PIN; else if (axis == AXIS_Y) pin = JOY_Y_PIN; else if (axis == JOY_SW) pin = JOY_SW_PIN; } return pin; } int getData(axis_t axis, device_t device) { pin_t pin = getAxisPin(axis, device); if (axis == JOY_SW) return digitalRead(pin); return analogRead(pin); } // Have 5 millis passed since last switch? bool clickWaitOver() { return millis() - clickTimer > 5; } void updateClickTimer() { clickTimer = millis(); } // Determine if button was just now clicked, unclicked, or no change. clickstate_t getSetClickState() { clickstate_t clickState = last_clck_state; if (clickWaitOver()) { // clickTimer = 0; // Reset timer bool clickedThen = last_clck_state.equals(CLK); // Input pullup makes for inverted button pressed state bool clickedNow = getData(JOY_SW, DEV_JOY) == LOW; if (!clickedThen && clickedNow) clickState = CLK; else if (clickedThen && !clickedNow) clickState = UNCLK; } updateClickTimer(); if (last_clck_state == clickState) return NO_CHANGE; last_clck_state = clickState; return clickState; } bool clicked() { return (getSetClickState()).equals(CLK); } bool unclicked() { return (getSetClickState()).equals(UNCLK); } // Read from given pin for given device, and convert to MIDI. byte getMidi(axis_t axis, device_t device) { int data = getData(axis, device); int MIDI_LO = 0; int MIDI_HI = 127; // For joystick, no change needed for the following initialized values. int dataLo = 0; int dataHi = 1023; byte midi = 255; if (device.equals(DEV_ACCEL)) { if (axis == AXIS_X) { dataLo = 278; dataHi = 413; } else if (axis == AXIS_Y) { dataLo = 260; dataHi = 408; } else if (axis == AXIS_Z) { dataLo = 270; dataHi = 412; } } // If checking Joystick only change midi if not checking switch. if (axis != JOY_SW) midi = map(data, dataLo, dataHi, MIDI_LO, MIDI_HI); return midi; } // Print MIDI data formatted to be read in Max. void sendMidi(axis_t axis, device_t device) { String prefix = ""; if (device.equals(DEV_ACCEL)) { if (axis == AXIS_X) prefix = "X"; // Ascii 88 else if (axis == AXIS_Y) prefix = "Y"; // Ascii 89 else if (axis == AXIS_Z) prefix = "Z"; // Ascii 90 } else if (device.equals(DEV_JOY)) { if (axis == AXIS_X) prefix = "x"; // Ascii 120 else if (axis == AXIS_Y) prefix = "y"; // Ascii 121 else if (axis == JOY_SW) { if (clicked()) prefix = "+\n"; // Ascii 43 else if (unclicked()) prefix = "-\n"; // ascii 45 } } byte midi = getMidi(axis, device); Serial.print(prefix); // 255 reserved for click. Only print midi if not for click. if (midi < 255) Serial.println(midi); } // Run initilization for Accelerometer void setupAccel() { pinMode(getAxisPin(AXIS_X, DEV_ACCEL), INPUT); pinMode(getAxisPin(AXIS_Y, DEV_ACCEL), INPUT); pinMode(getAxisPin(AXIS_Z, DEV_ACCEL), INPUT); } // Run initilization for Joystick void setupJoy() { pinMode(getAxisPin(AXIS_X, DEV_JOY), INPUT); pinMode(getAxisPin(AXIS_Y, DEV_JOY), INPUT); pinMode(getAxisPin(JOY_SW, DEV_JOY), INPUT_PULLUP); } void setup() { Serial.begin(9600); setupAccel(); setupJoy(); } void loop() { sendMidi(AXIS_X, DEV_ACCEL); sendMidi(AXIS_Y, DEV_ACCEL); sendMidi(AXIS_Z, DEV_ACCEL); sendMidi(AXIS_X, DEV_JOY); sendMidi(AXIS_Y, DEV_JOY); sendMidi(JOY_SW, DEV_JOY); delay(10); }
Code in the Max patch and subpatches
This clock-adjacent object visualizes my current energy level estimated by the ultradian rhythm.
Rotating Cam Visualization of Sine Wave
Linear Visualization of Sine Wave
There are a few main components to my assembly – the shaft with offset cams, the dowels that sit on top of the cams, the stepper motor mount, and the housing for the entire assembly. Below I will show my process for working on each one.
The Cam Shaft
I used a drill press to cut out 9 circles from a piece of plywood. I used a hand drill to drill a hole in each one so I could align them on the wooden dowel. Using glue, I arranged each circle to be offset from the one previous by about 1cm.
I tried making a smaller one, but found the distance between each cam was not enough to show the sine wave clearly.
The Rods and Rod Holders
Next, I had to figure out how to align the plastic rods above the circles. I rummaged around in the parts drawers at Products Studio and tried out many different arrangements of small metal pieces until I found something that I could space the dowels apart with. I considered using sewing bobbins (pictured left) but eventually settle on using a combination of square nuts and screw plates that I carefully arranged side by side to create the correct spacing for the plastic rods.
The Stepper Motor Mount and Gear Connection
Using Calipers and Bisecting Curves, I figured out the dimensions of the box and screw plate.
This part was very difficult, since if the gear is slightly off, the whole mechanism does not work. I tried many different to hold the stepper motor in place. First, I tried a simple angle bracket, but this was not secure enough. Next, I tried using the drill press to create a face plate but the measurements were just ever so slightly off. Finally, I created two laser-cut pieces of 1/8″ plywood stacked on top of eachother to allow for a counter-bore so the screw can sit flush. Instead of holes, I used ovals with a 1/4″ of wiggle room to allow for precision alignment and adjustment to ensure the gear sits perfectly in place.
The Housing
The housing was the least complicated and simply consisted of a 5 board box made of plywood. I used the tablesaw and a nail gun to assemble this.
Response to comments gathered during the in-class crit. Quote (verbatim) from at least two written critiques that you received (positive, negative, or otherwise) from the in-class crit, and respond to them. (You may agree or disagree—in either case, explain your position.)
“It’s soooo hard to build complex mechanical devices like this—congrats on even attempting it, but in a two-week project, I’m not surprised that you faced some real challenges completing this to your satisfaction.”
To this point, I would like to say that this was probably the biggest challenge I faced during this project. I think that this project taught me a lot about the role of mechanisms in ideation development. It is really difficult to bring a complex mechanical concept into reality since mechanisms require every aspect of an idea to be thought out, yet iteration is essential to the design process. It was really difficult for me to know what I should focus on next since everything seemed dependent on another part or piece of code. If I were to approach this project again, I would make sure that I had a functional mechanical prototype with all of the pieces that I wanted to have before moving forward. Since I continued to develop the mechanism while trying to tackle the code and the aesthetics, this project became very unwieldy. I am glad to have a better sense now of the significance of isolating mechanisms from the computation and design.
“I really like the idea of functional poetry; to make this poetic and functional at the same time. It brings art into practicality, which as an artist myself, I really appreciate. Now that you just now mention it, the fiber optic lighting would have been dope. Also, this inspired me to write a program to trace my own energy level.”
This comment felt really great to hear since I was really trying to negotiate between poetry and function with my object and I am glad that someone else resonated with that. Additionally, I am so happy to think I inspired someone else! It is a great reminder to me that a project does not need to be perfect to communicate with or even inspire another person. This is hard to remember in the face of many mechanical failures, so I really appreciate this comment.
“Maybe you could try out a simpler mechanical output that doesn’t require so many components. It will still be very beautiful just to simply have something moving linearly or something”
I think that this person definitely has a point, if I were to go back I think I would try to omit the pegs. That being said, my main mechanical output is really just the stepper motor. I think it was important for me that my visualization of this concept had some volume to it and I was not satisfied with simply linear depictions of these values. I would certainly revisit this idea and work to consider alternate representations of energy levels.
Are you happy with how the project came out? Did it satisfy your own goals? This should not simply be a recital of your process, but rather a meaningful reflection.**
While I am happy with my explorations during the process of this project, I am not satisfied with where the final project stands. Despite warnings about pursuing physical mechanisms, I had no idea how ambitious my undertaking was actually going to be. I spent so much of the project time trying to simply give my idea a physical form, let alone develop the technology to where it needed to be. A huge hurdle was simply getting the components I needed in a timely manner since I didn’t even know what parts I needed until I played around with the mechanisms for a few hours, googled issues I came into, and learned about new parts. I learned so much from this tinkering though – for example, I now know what a coupler does, the affordances of a D-shaft, and that there are different standards of screw sizes!
I plan to continue developing this project further since I have learned so much and feel that with more time, I will be able to create something I feel more satisfied with.
What you learned about your own abilities and/or limitations through the process of working on this project.
I confirmed some more things I already knew about my process – I tend to embrace a non-linear process, which is very difficult to make significant progress forward with. As I was warned about, I struggled with the mechanical aspect of this project. I have put significant time towards this project, both independently and in working one-on-one with not just Zach, but many hours with my studio shop advisor as well. Despite this, my project remains incomplete. I cannot help but feel a little disappointed that I did not get further with this project, but there were simply so many unfamiliar variables present.
In the last project, I took time to intentionally segment off short lo-fi prototyped versions of my idea (i.e. I made a simple version of my graphite resistor before trying to make it more complex). This helped significantly in reducing the number of variables I was working with. I think with this project, I didn’t quite know how to do a similar segmenting of the work which had a significant impact on my ability to complete the project.
Moving forward, I will make an even greater effort to segment the parts of my project and consult with knowledgeable advisors early on in the process, especially when involving mechanisms. After trying to jimmy-rig so many aspects of the project and then finally finding the right parts, I feel I have a better appreciation of precision tools. I will make greater efforts to attain these tools early on too.
Next steps. Do you expect to build another iteration of this project?
I am currently working on another iteration of this project. I have already made a lot of progress with refining the mechanism by taking the time to carefully measure all of the interlocking parts. This is in an attempt to get some closure on this rendition of my idea. I would certainly consider other visualizations of energy tracking and time. I also learned that I am fascinated by cam mechanisms and I would love to explore those further.
]]>
Overall image of the Automated Medicine Dispenser on my nightstand
The Automated Medicine Dispenser incorporates a wireless charger and phone stand into a single device that allows the user to dispense their medicine bottle upon stopping an alarm.
Overall photo showing the scale of the device relative to an iPhone.
Image of the internal wiring of the Automated Medicine Dispenser.
Depiction of the inside of the medicine holder. The cutouts help position the medicine bottle in the center of the holder in order to improve consistency in the optical proximity sensor readings.
Mounting position of the optical proximity sensor in the side wall of the medicine holder box. Electrical tape was used to protect the sensor housing and prevent unwanted wire crossings.
Shown above is the photoresistor actuated by the phone flashlight. The image above was taken in the phone’s resting position on the stand.
Depiction of the rainbow lights and servo actuation during the dispensing sequence.
The first major change from my original plan involved the tilting mechanism for the medicine bottle holder. In my original design, the medicine bottle holder incorporated a servo at the axis of rotation in order to complete the tilting motion (Note: original design shown below). This mechanism, however, proved to be infeasible because of the design requirements involving form factor. Since I wanted to have a flush front surface of the outer housing, the servo needed to be positioned further back requiring a different type of actuation mechanism. In addition to form factor, this original design also created the added challenge of aligning an axle on the other side of the holder with the center of rotation of the servo.
The original tilt mechanism actuated at the axis of rotation of the medicine bottle holder.
Upon reaching these previous conclusions about my original design, I switched to a linkage mechanism for the actuation of the tilt motion. This new design also came with its own challenges, mainly changing the original outer housing design to incorporate enough room for the servo in back and figuring out a reliable way to connect the linkage to the bottle holder. As shown below, the final mechanism incorporated popsicle sticks as the links and a needle for the pin joint with electrical tape forming the connections to the servo and bottle holder.
Linkage mechanism for the updated tilting mechanism.
Moving on to the surface of the wireless charger, the original design intended for this part to be hidden by a flat top piece that would allow the charging signal to penetrate through and reach the phone. In addition, this cover would also allow the light from the phone flashlight to penetrate and reach the photoresistor. A version of this design is pictured below using a relatively thicker paper that still allowed light to pass through.
Image of the original charger covering idea that was later removed.
While this design was more aesthetically pleasing, the final version did not include this covering and instead left the wireless charger exposed. The first reason contributing to this decision was the uneven bump that resulted from too small of a cutout for the charger (Note: bump pictured below). As a result of this bump, the covering was not able to be completely flat. In addition to this unevenness, the paper also reduced the ability to see the inner workings of the device which I felt was important to this prototype. Seeing the inner workings, as was confirmed by some in the critique, allowed for a better understanding of the complexity and resulted in a more robust looking device.
Side view of the surface bump on top of the phone stand.
During the critique of the project, there were many amazing points brought up about the overall design and aesthetic of my prototype. One critique about the placement of the proximity sensor stood out to me as it was something that I spent a lot of time thinking about. This person critiquing wrote that I should “Put the prox[imity] sensor under the pill bottle to sense when pill bottle has been removed/replaced.” This is something that I briefly considered while designing this mechanism but never really gave much thought to because it would interfere with the tilting mechanism and how far it could retract. Looking back on this decision, however, this would’ve been a much more accurate way of detecting whether the bottle had been taken because there is more variance in the sensor reading. When initially testing the final sensing mechanism, I had trouble getting the sensor to read a consistent change in the distance when the medicine bottle was taken which could have easily been solved by this new mounting point. In addition to this critique, another person also questioned “from a form factor perspective … [how] the two separate housings could be integrated into a single form.” Again, the housing form was something that I gave a lot of thought to during the ideation phase of this project and is something that I am very satisfied with about the final result. While it would look nice to have everything in a single form factor, I like the placement of the phone slightly below the top of the medicine dispenser section because it provides for a more natural feeling phone stand aesthetic. I felt that having the phone placed too high would result in an awkward looking final product.
Looking back on the process of this project, while the end result did not work out in exactly the way I had hoped in terms of reliability and overall aesthetic appeal, I was very satisfied with the functionality and proof of concept. From a reliability standpoint, the tilting mechanism did not meet the standards I was hoping to achieve due to the last minute changes to the entire design. In addition, I encountered a lot of trouble trying to time the retraction of the bottle holder in such a manner that didn’t break the flimsy pin joint constructed from a sewing needle.
Putting this project in perspective with my technical abilities, the biggest thing I learned is that mechanical aspects of a device are much more deceiving than they might seem. In beginning this project, I had a very clear image of the final product in my mind which didn’t change much throughout the process except in regards to the mechanical components. Upon getting further into the actual assembly of the final prototype I encountered many issues with the tilting mechanism that could have been solved much earlier on with a bit more thought on their design. Paying close attention to the mechanical aspects of the project is something that I would definitely change if I were to redesign this device in a future version.
While I do not plan on building another version of this project in the near future, the first thing I would do differently if I did build this project again would be to figure out a better system for dispensing the medicine. While my tilting idea is nifty, the physical constraints of the tilting action resulted in the final product falling out of line with my original idea.
Functional block diagram of the Automated Medicine Dispenser
Full electrical schematic of the Automated Medicine Dispenser
/* Medicine Dispenser Judson Kyle (judsonk) Collaboration and sources: 1) https://forum.arduino.cc/index.php?topic=8498.0 for rainbow blinking 2) https://courses.ideate.cmu.edu/16-223/f2020/text/exercises/electronics/photointerruptor/photointerruptor.html was consulted for the wiring of the proximity sensor Description: This device allows the user to dispense their medicine by utilizing the shortcuts on the iPhone to flash the flashlight for 1 second upon stopping their alarm. When the photoresistor detects this action, the servo pushes out the medicine holder while simultaneously flashing rainbow lights. The proximity snesor inside the medicine holder detects when the medicine bottle has been taken out and then placed back. Upon placing the medicine bottle back, the servo retracts the holder while simultaneously turning off the LED's. After all of this happens, the cycle is reset. Pin mapping: Arduino pin | type | description ------------|--------|------------- 3 output Red LED 5 output Green LED 6 output Blue LED 11 output Servo A0 input Photoresistor A1 input Optical Proximity Sensor */ #include <Servo.h> const int OPTPIN = A1; const int RED = 3, GREEN = 5, BLUE = 6; const int PHOTOPIN = A0; const int SERVOPIN = 11; //Thresholds int distThresh = 800, lightThresh = 600; int optVal = 0, photoVal = 0; int counter = 0; bool medicineTaken = false, bottleTaken = false; bool alarmSnoozed = false, dispenseMedicine = false, prev_bottleTakenState = false; //warningLights variables int timerVal = 3000; unsigned long waitingVal = 0; //Servo Variables Servo medicineTilter; bool tiltServo = false; int tiltAngle = 180; // Number of colors used for animating, higher = smoother and slower animation) int numColors = 255; // The combination of numColors and animationDelay determines the // animation speed, I recommend a higher number of colors if you want // to slow down the animation. Higher number of colors = smoother color changing. int animationDelay = 5; // number milliseconds before RGB LED changes to next color void setup() { //Output pins pinMode(RED, OUTPUT); pinMode(GREEN, OUTPUT); pinMode(BLUE, OUTPUT); //Input pins pinMode(OPTPIN, INPUT); pinMode(PHOTOPIN, INPUT); //Servo Intialization medicineTilter.attach(SERVOPIN); medicineTilter.write(0); Serial.begin(9600); } //Code to loop through void loop() { //Read sensor data optVal = analogRead(OPTPIN); photoVal = analogRead(PHOTOPIN); //Determine whether bottle has been taken or not if (optVal > distThresh) { bottleTaken = true; //If taken --> bottleTaken = true } else if (optVal <= distThresh) { bottleTaken = false; //If sensed in the holder --> bottleTaken = false } //Check for alarm snoozing action if (photoVal > lightThresh && alarmSnoozed == false) { alarmSnoozed = true; //Dispense medicine upon alarm snooze dispenseMedicine = true;//Dispense medicine upon alarm snooze medicineTilter.write(tiltAngle); } //Actions that follow detection of an alarm snoozed event if (dispenseMedicine == true) { // This part takes care of displaying the // color changing in reverse by counting backwards if counter // is above the number of available colors float colorNumber = counter > numColors ? counter - numColors : counter; // Play with the saturation and brightness values // to see what they do float saturation = 1; // Between 0 and 1 (0 = gray, 1 = full color) float brightness = 0.8; // Between 0 and 1 (0 = dark, 1 is full brightness) float hue = (colorNumber / float(numColors)) * 360; // Number between 0 and 360 long color = HSBtoRGB(hue, saturation, brightness); // Get the red, blue and green parts from generated color int red = color >> 16 & 255; int green = color >> 8 & 255; int blue = color & 255; setColor(red, green, blue); // Counter can never be greater then 2 times the number of available colors // the colorNumber = line above takes care of counting backwards (nicely looping animation) // when counter is larger then the number of available colors counter = (counter + 1) % (numColors * 2); // If you uncomment this line the color changing starts from the // beginning when it reaches the end (animation only plays forward) // counter = (counter + 1) % (numColors); delay(animationDelay); //Track when the bottle has been put back after taking the medicine if (prev_bottleTakenState == true && bottleTaken == false) { medicineTaken = true; waitingVal = millis(); } if (medicineTaken == true) { //Delay the reset to allow for safe return of medicine bottle without breaking linkage if (millis() - waitingVal > timerVal) { resetSystem(); //reset for next cycle } } } prev_bottleTakenState = bottleTaken; //Update previous bottleTaken state boolean } //---------Helper Functions----------------------- //Turn all LED's off void clearLEDs() { //Turn all LED's off digitalWrite(RED, LOW); digitalWrite(GREEN, LOW); digitalWrite(BLUE, LOW); } //Reset the system void resetSystem() { medicineTilter.write(0); //Return bottle holder to rest position clearLEDs(); //Turn LED's off //Reset state variables alarmSnoozed = false; dispenseMedicine = false; medicineTaken = false; } //Rainbow color changing helper functions copied from source 1 void setColor (unsigned char red, unsigned char green, unsigned char blue) { analogWrite(RED, red); analogWrite(GREEN, green); analogWrite(BLUE, blue); } long HSBtoRGB(float _hue, float _sat, float _brightness) { float red = 0.0; float green = 0.0; float blue = 0.0; if (_sat == 0.0) { red = _brightness; green = _brightness; blue = _brightness; } else { if (_hue == 360.0) { _hue = 0; } int slice = _hue / 60.0; float hue_frac = (_hue / 60.0) - slice; float aa = _brightness * (1.0 - _sat); float bb = _brightness * (1.0 - _sat * hue_frac); float cc = _brightness * (1.0 - _sat * (1.0 - hue_frac)); switch (slice) { case 0: red = _brightness; green = cc; blue = aa; break; case 1: red = bb; green = _brightness; blue = aa; break; case 2: red = aa; green = _brightness; blue = cc; break; case 3: red = aa; green = bb; blue = _brightness; break; case 4: red = cc; green = aa; blue = _brightness; break; case 5: red = _brightness; green = aa; blue = bb; break; default: red = 0.0; green = 0.0; blue = 0.0; break; } } long ired = red * 255.0; long igreen = green * 255.0; long iblue = blue * 255.0; return long((ired << 16) | (igreen << 8) | (iblue)); }
]]>
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.
]]>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(); } }
]]>
(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); }
]]>