llchen@andrew.cmu.edu – Intro to Physical Computing: Student Work fall 2019 https://courses.ideate.cmu.edu/60-223/f2019/work Intro to Physical Computing: Student Work Mon, 18 Nov 2019 06:22:36 +0000 en-US hourly 1 https://wordpress.org/?v=5.2.20 Team Jeff – Prototype Documentation https://courses.ideate.cmu.edu/60-223/f2019/work/team-jeff-prototype-documentation/ Mon, 18 Nov 2019 06:22:36 +0000 https://courses.ideate.cmu.edu/60-223/f2019/work/?p=8751 After our initial and follow-up conversations with Jeff, we decided to create an implement that would help him with his blind installation business. This post documents the process of building a prototype that demonstrates our idea.

Our initial meeting documentation can be found here.

Prototype:

Overall photo of the device in its off state.

Frontal view of the device at cool color temperature and low intensity.

Detail view of the top surface of the prototype, including the LCD display, knobs, and buttons.

Demonstrating the features of the prototype.

The device is intended to be a light source that is capable of simulating natural light based on different parameters of weather and time of day. It has two knobs that correspond to color temperature and intensity, as well three buttons that correspond to a sunny, cloudy, and sunrise/sunset preset condition. It was designed as a portable device that Jeff could bring to the homes of his clients and hold up behind different samples of blinds in order to demonstrate their unique qualities (e.g. blackout, semi-opaque, sheer).

Process:

This sketch shows the notes on the initial parameters we intended on incorporating in the prototype. The design of the prototype was also fleshed out through drawing.

We focused on getting the color temperature and intensity features working as a first step. This was done with the Arduino Uno, an LED strip, and two potentiometers.

Early on, we were considering a battery powered design for the device to be more portable. However, we decided to make a plug-in prototype in order to concentrate our efforts elsewhere.

In terms of design and fabrication, the CAD work was done in Fusion 360 and 3D printed. A progression of the design can be seen as follows.

We included holes to wall mount the encoders, buttons, and LCD screen. We also chose to add an indent on either side to provide an easier grip.

At this point, we began adding material appearances in order to visualize what the 3D printed outcome would look like.

We also tried to render the form with a emissive appearance that was intended to represent the panel of LEDs. However, it did not come out very well.

In unifying the electronic and fabricated components, we began by laser cutting a sheet of acrylic that fit inside the box, and bending the LED strip so that it covered the sheet. The LED strip had an adhesive backing so it was easy to apply. Later on, we had to remove the strip and remount it to a smaller sheet of acrylic, since this one did not fit.

This is a photo of all the components we had working at this point. After programming the encoders (which were previously potentiometers), we added the LCD display.

A close-up of the LCD display showing color temperature and intensity values.

A photo of Larry writing the code for the button presets.

A gif that shows a piece of support going flying after being chiseled off.

Discussion:

Various challenges faced the development of this prototype, from the initial design to its exterior appearance. We had struggled for days to find a suitable design that could be of use to Jeff, but we eventually came up with the idea of this light that could potentially aid in his professional business and be within the scope of our project. From there, the basic design for a prototype came fairly naturally, such as the features of tunable controls and a few convenient presets. But we faced a certain problem when trying to transition between controls and presets. The potentiometers we initially used had limitations on the extent they could rotate, making it hard for a seamless transition from a preset setting to whatever we tuned it to, as we had to jump to whatever the potentiometer position was when we tuned the color away from a preset value to ensure that the knobs wouldn’t be at a, let’s say ‘high’ value while displaying a lower value, as turning the knob higher wouldn’t allow it to go much further, despite having more range available that the LEDs could display. Encoders resolved this issue since they had no limitations on how far they rotated. We also encountered some issues with removing the support material on the casing, since the device needed to do so was out of commission. Thus, we chipped away at it by hand. The end result might’ve suffered slightly in appearance, but fortunately, it performed as desired.

After our formative critique with Jeff, we were able to gain many valuable insights into the future steps for our project. Jeff informed us that, although he had used simulate lighting demonstrations in the past, they were large setups that are hardly portable within the trunk of a car. Thus, a portable light source that could do something similar, albeit on a smaller scale, could give him an edge over competitors who wouldn’t be able to show them such a presentation. This can be especially practical in an often cloudy city like Pittsburgh or when no daylight is available, where it can be hard to show customers how some blinds might appear on sunny days, or how well they can block out intrusive artificial light from the neighbor’s yard or light from the interior to protect the customer’s privacy. Part of Jeff’s job is showing the customer what are the best blinds for their needs, and this could certainly help with that. Thus, our concept had been met with approval, and we were also able to receive significant critique on our design regarding how we could improve it to fit Jeff’s needs, making for a very productive critique session.

Critique Inputs and Moving Forward

Among the the many pieces of advice we had received, several important points stood out. Since we had been aware of this drawback in the prototype, we weren’t surprised when the subject of the lighting brightness and color was brought up. Our prototype was using an RGB LED strip, but it wasn’t close to intense enough to achieve bright enough lighting that could actually simulate how sunlight would appear, which we intended to save for the final version when we knew we had a good prototype concept. Jeff also requested that we change the dimensions from a fairly square width and height to a rectangular one. Furthermore, he was interested in a slightly elevated design so he wouldn’t have to hold it up, freeing his hands to demo his blinds. We proposed building a stand and using it as a storage device as well,  which worked for Jeff. Our prototype also didn’t diffuse the light enough, but we’ve got several solutions in mind, such as moving the light source further back from the frosted acrylic pane, since we had it fairly close in the prototype. Jeff had mentioned adding a heat feature of some sort, which we think the high intensity LEDs may make possible as an added bonus, although the intensity of this heat is uncertain.

Aside from those major goals, there were several other minor changes we also discussed. While we had initially brought up storing more presets with Jeff, which was a good idea, we eventually learned that he probably wasn’t going to need more than 3 presets like we had on the prototype, so any others could be tuned himself. Jeff had mentioned using a rocker switch as he didn’t need the fine control, but we concluded we would probably stick with the current input design as it worked anyways and didn’t suffer from bouncing. On the aesthetic side, Jeff requested that it come in black, which would be doable. While he had been fine with the tactile interface as it met all practical needs, it was not without possible improvements. We considered that slightly larger knobs might be nice, but since we had already ordered buttons previously that were similar to our current ones, we were probably still going to use them.

Overall, our priorities were going to be programming and wiring up a new light source, setting up new presets and adjusting the acrylic window to approximate real world lighting conditions, adjusting the dimensions, and building a stand. Following those will largely be aesthetic goals that we hopefully can accomplish after taking care of our priorities.

 

Timeline:

Fri. Nov. 15 – Research and submit request for brighter LED module

Sun. Nov. 17 – Finalize CAD for new design, submit to Stratasys

Tue. Nov. 19 – (in class) If printing is complete, remove support material. Otherwise, design and laser cut acrylic pieces (LED backing, diffusing cover, stand elements). Programming and hardware.

Thu. Nov. 21 – (in class) Design and laser cut acrylic pieces. Programming and hardware.

Sun. Nov. 24 – All electrical components finalized and working, stand fabrication complete.

Tue. Nov. 26 – (in class) Finish 3D print (sand and spray paint)

Thu. Nov. 28 – (in class) Finish 3D print (sand and spray paint)

Fri. Nov. 29 – Finishing and spray paint complete

Sat. Nov. 30 – Final assembly

Tue. Dec. 3 – Final project crit.

]]>
Team Jeff – Initial Meeting https://courses.ideate.cmu.edu/60-223/f2019/work/team-jeff-initial-meeting/ Mon, 28 Oct 2019 17:40:41 +0000 https://courses.ideate.cmu.edu/60-223/f2019/work/?p=8652 Intro:

With the end goal being an implement that sets out to improve some aspect of life for Jeff, we approached this initial meeting with the objective of finding possible leads for what such an implement might be like. The meeting was conducted on Sunday, October 20th, from 1:00-2:00 PM at Jeff’s home. The entirety of Team Jeff was present, with Emily as the interviewer, Jeff as the interviewee, and Larry as the note-taker.

Agenda:

The proposed agenda that was drafted up collaboratively in Google Docs.

Summary:

During our on-site interview, Jeff was of great help in the interview process, and we were able gain some insight into Jeff’s daily life and see the inside of his home.

He offered us a possible design that his son had come up with for a project. It was certainly appreciated that we had a design available, but unfortunately, it was rather beyond the scope of what our project could encompass.

Despite that, we were still able to have a very fulfilling interview.

As a successful owner of a custom blinds business, he’s proud of it and has every reason to be. For 12 consecutive years, his company has been awarded the super service award by Angie’s list, which fewer than 5% of companies on the site receive. This reward indicates exceptional ratings by his customers, which speaks to the quality of work he does and the quality of the product he installs in customers’ home.

Examples of his work take no more than a glance toward any window, with some of them being motorized and remotely controlled. He’s considering connecting up his Amazon Alexa to the blinds as well, so it’s not hard to tell that Jeff is no stranger to technological devices assisting in his daily life. Below is a video of a motorized blind that was very similar to one we saw in Jeff’s house.

While his desk might be piled high with papers and miscellaneous items, Jeff keeps his schedule well-organized with his phone, as expected of a professional. Many of his tools of the trade already have their own toolboxes and binders. Among equipment he mentioned were these clasps as shown below that come in a variety of colors, which he keeps in a bag. There are certainly organization solutions to organizing these clasps, but it’s hard to envision making computing play an important role for such a task.

Source: https://images-na.ssl-images-amazon.com/images/I/51Qg8KbGnAL._SX425_.jpg

Jeff’s grandchildren visits him fairly frequently, and he keeps them entertained by going out on excursions or with the arts-and-crafts box in their home.  Perhaps an interactive game could be interesting for his kids, though they’re in an age range that’s phasing out of playing with toys and the like, so it may not be all that functional.

We were also given a chance to check out Jeff’s car, which he uses to commute to his clients. Though the trunk looks a bit cluttered, it certainly has just about anything he needs. A binder of samples lie in the middle and a ladder that looks like its connected a scaffold allows him to walk along an elevated platform the width of most window to install brackets or whatnot.

The inside of Jeff’s car.

Takeaways:

It’s going to be a challenge to come up with something that Jeff would have a use for. At least, if he had a need for anything, it seems like he already has a good solution for it.

A device intended for infrequent use in the home is unlikely to be of use to Jeff because he and his wife keep themselves busy, often spending time outside their home.

Thoughts:

I’d say our interview went quite smoothly and felt fairly natural. We were able to learn much about Jeff’s life and share a little about ourselves in the meantime, and we were able to get a glimpse into what his home and daily life were like. Though we were able to ask just about everything on our plan in one way or another, we struggled to come up with even a few areas in his life that could really benefit from a device we could make.

Perhaps we should’ve inquired further into what sort of device would be of use to his wife, or perhaps we could’ve discussed whether a device would’ve been of use in the backyard we caught a glimpse of beyond the back porch sliding doors.

We decided to follow up with an email posing a few more questions to Jeff and his wife which will hopefully lead us to an entry point for a useful implement.

]]>
Physical Pomodoro Clock https://courses.ideate.cmu.edu/60-223/f2019/work/physical-pomodoro-clock/ Thu, 17 Oct 2019 02:37:46 +0000 https://courses.ideate.cmu.edu/60-223/f2019/work/?p=8316 Overview

The Physical Pomodoro Clock is a productivity tool disguised as a laptop stand that is designed to assist the user in staying focused over longer work periods by allowing the user to set a goal for how they would like to divide their productive time between working and taking healthy breaks as well as reinforcing achieving that goal through incentivizes for getting closer to the goal and reminders for the user when they stray from their goal.

Operation

Here’s a rundown of how the Physical Pomodoro Clock works.

It’s probably best to understand how the Pomodoro system itself works, which is a very simple concept. Break up the entire time you intend to work into 30 minute blocks. Work for the first 25 minutes, then take a break to stretch and move around for the next 5 minutes. Rinse and repeat for every 30 minute block.

The Physical Pomodoro follows a similar concept. While the clock is running, the user will either be in work or break mode. It’s up to the user to let the clock know when they’re doing what, and the clock will keep track of the proportion of time they spend working or on break. When a 30 minute cycle completes, the clock will compare the user’s work-to-break ratio, measured as a percentage, to the target ratio that the user can define as they please. The closer the user’s work-to-break ratio is to the target ratio, the more tokens they are awarded with. These tokens can then be spent for a random chance that a piece of candy is pushed out from the computer stand to award the user for their productivity and encourage them to keep at it.

Interacting with the Clock

Viewable on the LCD screen, the user can utilize two menus, the main pomodoro clock menu and a ‘popup’ menu for spending tokens, the virtual currency awarded for completing work cycles.

When interacting with the clock, there are two buttons and a knob.

  • The knob’s sole purpose is to set the target ratio.
  • The red button serves the following purposes
    • pressing it allows the user to start the clock from a paused state, resume work time from break, and spend tokens
    • holding it allows the user to access the token spending screen
  • The black button serves the following purposes
    • pressing it allows the user to start their break and exit from the token spending screen
    • holding it pauses the clock and resets the current cycle’s progress

Understanding the Clock Screen
The clock state

In the bottom right of the clock screen, there’s an indicator of whether the clock is running or not. If it is running, a “>” symbol will be shown and the clock time and progress indicators will be changing. If it is not running and thus in its paused state, a “||” symbol will appear. The clock as well as any progress on the current cycle will be set to 0 when it is paused.

Another useful indicator is the work vs. break indicator in the third column. If the current set mode is break, then there will be  a “B” in the top row of the third column. Otherwise the mode is work, so there is a “W” in the bottom row of the third column.

The ratio system

The clock menu displays a graphical representation of the user’s work-to-break ratio in the left-most column and the target ratio in the column to the right of it. The left side of each column shows work percentage while the right side shows the break percentage. The work time percentages are also shown numerically in the top row, where the first percentage, which has a checkered ‘target’ icon before it is the target ratio, while the user’s ratio is at the far right, which seems to be updating to a mid-20 percent at the moment the photo was taken, thus the furthest left column is around 1/4 of the way full on its left side, but there’s nothing on its right side since no break time has been taken in that cycle.  The target ratio at 94% is represented by a completely filled left half in the second column with not enough break time to be shown in the right half.

The ratio system has been scaled such that it has a minimum of 50% and a maximum of 95%.

Measures of overall progress

Note: The cycle and token values are stored on the device and persist even after powering the device off and on.

The remaining two unexplained parts are the numbers followed by “cy” and the coin symbol followed by a “x” and a number. The first set of numbers is an indicator of purely how many cycles have been completed, irregardless of ratios achieved or not achieved. The second number is the number of total tokens the user has available to spend on the token menu.

The token Menu

I’d say this menu is pretty self-explanatory. If you press the red button, it’ll deduct 10 tokens and do some probability math to decide whether it gives you a prize or not.

The Notification System

The clock can notify the user through the use of a vibrating motor which is mounted in the lower right corner of the above photo, as well as flashing an LED in the bottom left corner of this photo.

When the clock is turned on, put into play, or completes a cycle, it’ll do one short buzz.

When the clock wants to remind the user to take a break or get back to work because they’re too far from their target ratio, it repeatedly makes short buzzes until the user complies or the cycle completes.

When the user wins a prize from spending their tokens, a long buzz will sound from the clock to commemorate the moment.

Videos

Video of switching menus

Video of spending tokens for reward

Process and Review

This project began as one of several proposed ideas for something that could be useful in my life. The initial idea was a vague design based on a Pomodoro clock  with very minimal input, just 2 buttons and a potentiometer.

However, a mess of wires, electronic components, and boards aren’t exactly aesthetically appealing, so as the electronic circuit was nearly finished, I had to look for a suitable structure to mount the simple clock. As the goal of the project was to simply design something useful, I figured I’d mount the Pomodoro clock somewhere it would be most effective and give it a second purpose. A major reason I got a Pomodoro clock on my phone is how long I spend in front of a computer without getting up to move, which can be unhealthy. So I figured a computer stand could be a practical means to elevate my computer screen to a more comfortable level while being right where I need it most.

A picture of the WIP CAD design for the stand from the Fusion 360 software can be seen below.

Works great as a laptop stand, 1/4 in. plywood is pretty sturdy

In the later hours of working on this project, I had wavered on whether to keep or discard a reward system, where a virtual currency could be earned by following the Pomodoro clock closely. In the end, I decided I wanted to implement one and did a somewhat rushed job in adding a servo and some other parts to randomly reward the user with a candy when they spent the currency. Unfortunately, the candy I used did not fit very well through the hole it was designed to leave through, and the servo struggled to push the candy very far against friction. Perhaps if I had a different candy or designed the case to be larger to allow for more options, then it would’ve gone more smoothly.

 

A rainbow tangle of wires, normally kept hidden underneath the top panel of the laptop stand. The servo, candy, and, and part of a popsicle stick ramp can be seen on the right side, adjacent to the polycarbonate ‘window’.

Although much of the code ran smoothly, there were a few hitches. I had also uploaded a piece of code I had slightly modified but didn’t test thoroughly, thinking that there was no major affect. However, the notification system went off even at times it shouldn’t be during the demo, which made it rather difficult to demo the timer aspect due to the incessant buzzing from the vibration motor. But there was a very curious bug earlier in development when Japanese characters would appear, which was due to incorrect mapping to certain memory addresses of the LCD screen since the LCD screen came with Japanese and English characters by default that could be displayed on the screen. The intended goal was a few custom character slots that I had been writing the custom ratio indicator symbols to.

Discussion

Response

“It would be awesome if things are drilled onto the board instead of being taped.”

– I admit that the tape did not do as good a job as I hoped to hold things together. If I had more confidence in where things should be located, I may have gone ahead and glued it at least. Drilling wouldn’t be too bad for some components, but the breadboards I was using didn’t seem to be easily mounted in that manner, so I’d probably still avoid drilling.

“I wish we got a little more idea on how the timer itself worked.”

– And I wished I had given you a better idea about how it worked. That was just a flaw in my presentation skills after panicking from the notification system incorrectly going off while the timer was running. There was actually a lot of other functionality I wasn’t able to show, including a graphical representation of the ratios in the left two columns of the LCD, a break/work indicator column, and a play/plause indicator in the bottom right. But the basics are about the same as the classic Pomodoro clock. The ratio potentiometer adjusts a target goal to break out of the hard set 25-5 minute ratio, instead letting the user define the proportion of work they want to get done with each cycle. With the system of letting the user indicate whether they’re on break or working, my system gives the user more freedom in defining when they work and when they take a break, so the break doesn’t have to be at the end of the cycle. When the cycle completes, a productivity ratio closer to the self-chosen target ratio would award more tokens, which seem to be an aspect that received much praise, despite the system of reward delivery being close to non-functional.

“The circular acrylic window for the rectangular LCD display doesn’t make to much sense to me”

– I thought it would look cool to be able to see inside at the electronics underneath. Unfortunately, there’s not much in terms of LEDs or anything else to see, so it probably would’ve been better to stick to a square hole that would’ve made mounting it infinitely easier. So I certainly agree that the acrylic window was fairly unnecessary, though I think it does kind of look cool.

“Does it only hold one piece of candy at a time?” + “Try to use a smaller and lighter candy”

– Sadly yes, it holds only one piece of candy, since I was rather constrained by the space within the computer stand. If I was feeling more creative, I might play it off as a feature to encourage the user to get up and refill the candy each time instead of sit in front of the computer the whole time. If I were to do it again, especially if I wanted to hold more than one candy, I’d probably use a smaller and lighter candy that came in a shape that could easily roll, like a tic-tac, though I would have had to go buy the candy then.

“Nice popsicle stick” – Indeed.

Critique

After having completed the project, I can look back and say that there were at least two major problems. The first was creating a design that suffered too much from feature bloat. The design would’ve greatly benefited from retaining the simplicity of a basic Pomodoro clock to go with its simple controls. The whole reward system was an entertaining concept, but it appears to be detrimental to add unnecessary features that take away from the intentionally simple design. It probably would’ve been better to have used the second menu option for more in depth clock configurations.

The other problem is a general lack of planning for a number of issues. These included somewhat minor issues: a lack of space for the electronics to easily fit beneath the top board of the stand, not cutting certain openings a little smaller to account for the width of the laser, not laser cutting a slot for the small ‘tab’ in the potentiometer to fit into, as well as somewhat major ones: lack of a plan for mounting the devices and unclear aesthetic goals for the project. While the initial idea was feature-rich, it was lacking in implementation specifics.

Conclusion

This project has been a very valuable learning experience. The many issues that I didn’t anticipate this time around are something I can learn from and hopefully address when I can anticipate them in future projects and avoid making the same mistakes this time around. I also had to think outside the lasercut box for this project, opting for a laptop stand instead of a generic box. So at least, even if the Pomodoro clock doesn’t alway work, I can rely on it as a fine laptop stand. If I had another shot at it, I’d see if I could make it bigger with clear acrylic and set up a better candy delivery system, probably with a steeper ramp and a servo controlled valve that delivered smaller and rounder candies. The clear acrylic would let you could see the electronics working inside the stand as well as the candies moving around, which I think would be cool, plus the whole stand could pulse with the color of the internal LED when the user was being notified. I’d also see if I could fix the bug where the notification system goes off all the time when the intended behavior is to notify the user when they should end their break. At least it reminds the user to start their breaks and when a cycle ends, just like a classic Pomodoro clock.

Technical

Schematic

Code

/*
Physical Pomodoro Clock

Description: Code for taking input from two buttons and a potentiometer to control a pomodoro clock with a notification and reward functionality

Pin Mapping
Input
Pin |  Input
2      Button 1 (RED)
3      Button 2 (BLACK)
A0     Potentiometer
A5     Random Noise Pin

Output 
Pin | Output
5     Notification system (Vibration Motor + LED)
7     Servo Pin

//Referenced resources
   LCD Screen code contains snippets and references to code written by Robert Zacharias at Carnegie Mellon University, rzach@cmu.edu
   released by the author to the public domain, November 2018
//https://forum.arduino.cc/index.php?topic=418257.0
//https://forum.arduino.cc/index.php?topic=215062.0
//https://learn.robotgeek.com/28-robotgeek-getting-started-guides/61-robotgeek-i2c-lcd-library#customChar
//https://learn.robotgeek.com/getting-started/59-lcd-special-characters.html
*/




#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Servo.h>
#include <EEPROM.h>

/* Create an LCD display object called "screen" with I2C address 0x27
   which is 16 columns wide and 2 rows tall. You can use any name you'd like. */
LiquidCrystal_I2C screen(0x27, 16, 2);

Servo gate;

//Pins
const int BUTTON1_PIN = 2;
const int BUTTON2_PIN = 3;
const int RATIO_PIN = A0;
const int NOTIFIER_PIN = 5;
const int REWARD_PIN = 7;
const int RANDOM_PIN = A5; //Intentionally unconnected to be a source of random noise
//***


//***Utilities***

//Mode
bool isPomo = true;

//Notifier
bool canNotify = false;
unsigned long notifyTimer = 0;
unsigned long endTimer = 0;
bool flop = false;
int flopFreq = 0;
int duration = 0;

//Masks
const int B1P_MASK = B0001;
const int B1H_MASK = B0010;
const int B2P_MASK = B0100;
const int B2H_MASK = B1000;

//Time constants
const int delayRate = 100; //Refresh rate in ms for the whole system
const int renderRate = 800; //rate of rendering

//Max Pot Value
const int MAX_POT = 1023; //Represents the highest potentiometer output

//Buttons
//tracks how many delays
int counter = 0;
//The minimum time in ms necessary for a press to count as any press
const int minPressTime = 300;
//The minimum time in ms necessary for a hold to register
const int minHoldTime = 2000;

//int[] for button hold times
unsigned int button_Times[2];

//Reward Gate parameters
const int gateTime = 5000; //2 seconds to grab the candy if won 
const int openAngle = 5;
const int closedAngle = 90;


//***Pomodoro
unsigned long referenceTime = 0; //Whenever the clock is 'paused', this number is no longer useful and must be reset on resuming pomodoro. Thus a pause resets progress on that pomodoro.
//For higher accuracy on progress time
unsigned long lastTick = 0;
unsigned int elapsed = 0; //Once elapsed in seconds reaches the equivalent of 30 minutes, a cycle will be updated, and 'prizes' awarded. Then it will be reset
unsigned long progress = 0; //productive time in milliseconds
unsigned int cycles = 0;
const unsigned int CYCLE_CONSTANT = 60; //number of seconds in 30 minute pomodoro
//const byte pRatios[6] = {1, 2, 4, 5, 9, 14}; //productivity ratio numbers to 1
float ratio = 0.83; //number of productive seconds
//Ratio limits. Max break is 15 min, while min break is 2 min per 30 minute pomodoro
const float minRatio = 0.5;
const float maxRatio = 0.95;


//ratio and progress percentages respectively
byte rPercent = 0;
byte pPercent = 0;

bool isPaused = true;
bool isBreak = false;

//byte columns
byte left_c = B11000;
byte right_c = B00011;

//symbol like > to indicate clock is running #Not Used
byte playSign[] = {
  B10000,
  B11000,
  B11100,
  B11111,
  B11100,
  B11000,
  B10000,
  B00000
}; // (note the extra row of zeros at the bottom)

//symbol like || to indicate clock is paused
byte pauseSign[] = {
  B10001,
  B11011,
  B11011,
  B11011,
  B11011,
  B10001,
  B00000,
  B00000
}; // (note the extra row of zeros at the bottom)
//***

//Not Used
byte upSign[] = {
  B00000,
  B00100,
  B01110,
  B11111,
  B00000,
  B00000,
  B00000,
  B00000
}; // (note the extra row of zeros at the bottom)

//Not Used
byte downSign[] = {
  B00000,
  B00000,
  B00000,
  B11111,
  B01110,
  B00100,
  B00000,
  B00000
}; // (note the extra row of zeros at the bottom)

//Resembles a coin
byte rewardSign[] = {
  B00100,
  B01010,
  B11101,
  B11101,
  B11101,
  B01110,
  B00100,
  B00000
}; // (note the extra row of zeros at the bottom)

//Resembles a target
byte targetSign[] = {
  B00000,
  B00100,
  B01010,
  B10101,
  B01010,
  B00100,
  B00000,
  B00000
}; // (note the extra row of zeros at the bottom)

/*
  //This is adjustable and determined by code
  byte targetSign[] = {
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000
  }; // (note the extra row of zeros at the bottom)

  //This is determined by code and updated as progress changes
  byte progressSign[] = {
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000
  }; // (note the extra row of zeros at the bottom)
*/

//***Reward System
unsigned int tokens = 100; //Total 'currency'
unsigned int spinPrice = 10; //Price to attempt to draw a prize
bool trySpin = false; //Indicates whether an attempt to draw a prize is made
const int randomThreshold = 40; //Percent chance that prize is won 
//***


struct saveData {
  unsigned int savedCycles;
  unsigned int savedTokens;
};


void setup() {
  Serial.begin(9600);

  saveData lastSave;
  //Assumed that EEPROM will read and write from first address
  EEPROM.get(0,lastSave);
  Serial.println(lastSave.savedTokens);
  cycles = (lastSave.savedCycles!=65535)?lastSave.savedCycles:0;
  tokens = (lastSave.savedTokens!=65535)?lastSave.savedTokens:0;
  
  //Random Seed setup
  randomSeed(analogRead(RANDOM_PIN));

  //Setup Pins
  //Buttons
  pinMode(BUTTON1_PIN, INPUT);
  pinMode(BUTTON2_PIN, INPUT);
  //Potentiometer
  pinMode(RATIO_PIN, INPUT);
  //Audio-visual Notification
  pinMode(NOTIFIER_PIN, OUTPUT);
  gate.attach(REWARD_PIN);

  //turn gate to closed position initially
  gate.write(closedAngle);
  
  // initialize the screen (only need to do this once)
  screen.init();

  // turn on the backlight to start
  screen.backlight();

  //Loads symbols into memory
  screen.createChar(0, playSign);
  screen.createChar(1, pauseSign);
  //2 is reserved for both up and down signs and dynamically set later
  screen.createChar(3, rewardSign);
  screen.createChar(4, targetSign);
  // set cursor to home position, i.e. the upper left corner
  //screen.home();

  screen.clear();
  screen.home();
  //screen.print("b1 = ");
  //screen.setCursor(0, 1);
  //screen.print("b2 = ");
  digitalWrite(5, HIGH);
  delay(1000);
  digitalWrite(5, LOW);
  //tokens = 100;

}

void loop() {
  
  //screen.clear();
  delay(delayRate);

  //screen.setCursor(5, 0);
  //screen.print(digitalRead(2));

  //screen.setCursor(5, 1);
  //screen.print(digitalRead(3));
  //Serial.println(analogRead(RATIO_PIN));

  byte buttonData = buttonInput();
  //Button Data: ####
  //Last # (2^0): B1 Press
  //Left1# (2^1): B1 Hold
  //Left3# (2^2): B3 Press
  //Left4# (2^3): B4 Hold
  //A short hold has short hold logic
  //A long hold has long hold logic

  //if it isn't paused, process data from last loop
  if (!isPaused)pomodoro_tick();

  //Divides logic based on whether the mode is Pomodoro or not
  if (isPomo) {
    //updates various internal variables for this display mode
    pomodoro_logic();
    //Render to screen step
    if (counter > renderRate)renderPomo();

    //Button press logic
    //If the 1st button has been pressed and released, ***PLAY***
    if ((buttonData & B1P_MASK) != 0) { 
      //start/resume functionality
      //Start from paused
      if (isPaused) {
        isPaused = false;
        //Re-syncs the reference time
        referenceTime = millis();
        //syncs up the lasttick initially
        lastTick = millis();
        //enables notifies
        enableNotify();
      }

      //Resumes from break
      if (isBreak) isBreak = false;

    }
    //If the 1st button has been held, ***SHOP***
    if ((buttonData & B1H_MASK) != 0) { 
      //Switch menus
      isPomo = !isPomo;
      //Limits clear commands to when screen changes
      screen.clear();
    }

    //If the 2nd button has been pressed, Mark Break  ***BREAK***
    if ((buttonData & B2P_MASK) != 0) {
      isBreak = true;
    }
    //If the 2nd button has been held, Mark Pause/End ***PAUSE***
    if ((buttonData & B2H_MASK) != 0) {
      isPaused = true;
      //reset all current progress and time, this way there won't be any surprises when resyncing resets progress
      progress = 0;
      elapsed = 0;
      //Disable notifies
      disableNotify();
    }




  }
  else {
    //shop Logic, only relevant when in shop, so does not need to be in main loop body
    shopLogic();
    //Render Entertainment
    if (counter > renderRate)renderShop();

    //press button 1
    if ((buttonData & B1P_MASK) != 0) {
      //the spend your token sort of fun stuff
      trySpin = true;
    }
    //press button 2
    if ((buttonData & B2P_MASK) != 0) {
      //returns to main screen
      isPomo = !isPomo;
      //Limits clear commands to when screen changes
      screen.clear();
    }


  }



  //Ensures that counter always increments, so refresh doesn't get stuck at menu changes
  counter += delayRate;
}

//Relevant Logic for the Shop
void shopLogic() {
//Spin button pressed
 if (trySpin) {
    //Tokens are charged
    if (tokens >= spinPrice) {
      tokens -= spinPrice;
      generateReward();
      updateSave();
    }
    else {
      //show inssuficient funds
      //Serial.println("fail");
      screen.clear();
      screen.home();
      screen.print("Insufficient ");
      screen.write(3);
    }
    trySpin = false;
  }
  
}
void renderShop() {
  counter = 0;
  //screen.clear();
  screen.home();
  
  //Default Screen
  
  //Top Row
  screen.print("Token Spin | Own");

  //Bottom Row
  screen.setCursor(0, 1);
  screen.print("Use ");
  screen.write(3);
  screen.print("x" + (String)spinPrice);
  screen.setCursor(11,1);
  screen.print("|");
  screen.write(3);
  screen.print("x" + (String)tokens);

}
//to be implemented
void generateReward() 
{
  
  screen.clear();
  screen.home();
  int randNumb = random(100); //Generates a random number from 0 to 99 (100 possible numbers)
  if(randNumb<randomThreshold){ //If the number is less than the threshold, or percent chance to win, then the user wins a prize
    gate.write(openAngle); //Opens the prize gate
    screen.print("You Win a Prize!");
    digitalWrite(NOTIFIER_PIN, HIGH); //Some other indications of winning
    delay(gateTime); //Time to claim prize
    gate.write(closedAngle); //Closes the prize gate
    digitalWrite(NOTIFIER_PIN, LOW); //Silences the notification
  }
  else{
    screen.print("...Try again?"); //A somewhat encouraging message in case of loss
    delay(1000);
  }
  
}


//byte Cells[2][8] = {}; //fill rows 0-6 of 1D arrays
//byte Cell1[8] = {};
//byte cell2[8] = {};

//print double column (each cell has 7 rows)
void RatioPercent(int column, float r1, float r2, byte slot) {
  //These individual byte arrays may be converted to a 2D array
  //byte topCell[8]; //fill rows 0-6
  //byte lowCell[8]; //fill rows 0-6

  int divisions = 7;
  int cells = 2;

  //starts at the bottom of the cell, row 1
  for (int j = cells - 1; j >= 0; j--) {
    //j = cell index, therefore
    //j is inverse for left
    //j is direct for right
    byte temp[8] = {};
    for (int i = divisions - 1; i >= 0; i--) {
      //i begins at the bottom rows, at index 6

      //left: the relative number of rows that should be lit : Right (cells begin from the bottom, as do the row writing, thus thye have to be subtracted)
      if (r1 * divisions * cells > (cells - j - 1)*divisions + (divisions - i)) {
        //Cells[j][i] = Cells[j][i]|left_c;
        temp[i] = B11000;
      }
      //
      //At the bottom ,it would be all previous cells plus all rows of last cell (j*divisions) + i rows
      if (r2 * divisions * cells > (j)*divisions + i + 1) {
        //Cells[j][i] = Cells[j][i]|right_c;
        temp[i] = temp[i] | B00011;
      }

    }
    screen.createChar(slot + j, temp);
    //Print current cell
    screen.setCursor(column, j);
    screen.write(slot + j);
  }
}


//Renders the screen for the pomo mode
void renderPomo() {
  //Resets the counter for screen refresh
  counter = 0;

  //clear the screen
  //screen.clear();
  //Personal Progress/ratio (column 0, both rows)
  RatioPercent(0, pPercent / 100.0, (float)elapsed / CYCLE_CONSTANT - pPercent / 100.0, 5);

  //Target Progress/ratio (column 1, both rows)
  RatioPercent(1, rPercent / 100.0, 1 - rPercent / 100.0, 7);

  //Break or work (column 2, both rows)
  screen.setCursor(2, 0); //isBreak is true (top)
  screen.print((isBreak) ? ("B") : (" ")); //Prints B for break on top or empty for not break
  screen.setCursor(2, 1); //isBreak is false (bottom)
  screen.print((isBreak) ? (" ") : ("W")); //Prints B W for work on bottom or empty for on break
  //target percent (column 3-6, row 0)
  char char_buffer [6]; // a few bytes larger than your intended line
  sprintf (char_buffer, "%02d", rPercent);
  screen.setCursor(3, 0);
  screen.write (4); //target sign, index 3
  screen.print (char_buffer); // index 4,5
  screen.write (37); //percent symbol, index 6

  //# of cycles (column 4-7, row 1)
  screen.setCursor(4, 1);
  //screen.print(cycles);
  sprintf (char_buffer, "%02d", cycles); //number of cycles. May go into the 10s, but assumed that using this for 100 cycles or 3000 continuous minutes is unlikely
  screen.print(char_buffer);
  screen.setCursor(6, 1);
  screen.print("cy"); //for cycles

  //Prints time string (column 7-11, row 0)
  //char char_buffer2[6];
  sprintf (char_buffer, "%02d%s%02d", elapsed / 60, ":", elapsed % 60); // send data to the buffer
  screen.setCursor(7, 0);
  screen.print(char_buffer);

  //print reward string (column 9-13or14, row 1)
  screen.setCursor(9, 1);
  screen.write(3); //reward sign, index 8
  sprintf (char_buffer, "%03d", tokens);
  screen.print("x");
  screen.print(char_buffer);

  //indicator of current percent (c12-15, r0)
  //Writes percentage
  screen.setCursor(12, 0);
  if(pPercent<100){ //to avoid three digits pushing the line off the screen
    sprintf (char_buffer, "%3d", pPercent);
    screen.print (char_buffer); // index 4,5
  }
  screen.write (37); //percent symbol, index 6
  
  /*
  screen.setCursor(12, 0);
  //print indicator of more work or more rest
  //threshold is hard set as 5 currently, can be serialized as a const variable if necessary
  if (pPercent - rPercent > 5) {
    screen.createChar(2, downSign);
    screen.write(2);
  }
  else if (rPercent - pPercent > 5) {
    screen.createChar(2, upSign);
    screen.write(2);
  }
  else screen.print(" ");
  */
  
  //indicator of pause or play status
  screen.setCursor(15, 1);
  screen.write((isPaused) ? (1) : (62)); //if paused, print pause symbol, if playing, use > for play symbol (62).
}

//Pomodoro Logic to keep variables updated while clock functionality is in play
void pomodoro_tick() {
  //temp long
  //moves progress if not on break
  elapsed = (unsigned int)((unsigned long)(millis() - referenceTime) / (unsigned long)1000);
  if (!isBreak)progress += millis() - lastTick;
  lastTick = millis();
  
  //Loops pomodoro once time reaches limit
  if (elapsed > CYCLE_CONSTANT) {
    elapsed = 0;
    progress = 0;
    cycles += 1;
    calcReward();
    referenceTime = millis();
    updateSave();
  }

  //Reminder Logic
  //Operate on the NOTIFIER_PIN
  
  //Long LED and vibration for when cycle restarts
  if(elapsed == 0){ //an indicator that a cycle restart occurred
      
      startNotify(1000, 0);
  }
  
  //Short pulses when break should begin
  if((!isBreak)&&(pPercent-rPercent>5)){
    if(duration==0)startNotify(1000,1);
  }
  
  //Short pulses when work should resume
  if((isBreak)&&((int)((float)(elapsed*100)/CYCLE_CONSTANT-pPercent)-(100-rPercent)>5)){
    if(duration==0)startNotify(1000,1);
  }


  if(canNotify)writeNotify();
  

}

//Relevant logic that occurs in pomodoro screen mode but not related to its ticking (clock) functionality
void pomodoro_logic() {
  
  ratio = analogRead(RATIO_PIN)*(maxRatio - minRatio)/MAX_POT+minRatio; //Sets the ratio proportional to input potentiometer signal within the range between min and max ratio
  
  rPercent = (int)(ratio * 100);
  pPercent = ((int)(progress / (unsigned long)10) / (CYCLE_CONSTANT));
  
  
  
}

//Determines Reward for completing a pomodoro
void calcReward() {
  //Uses rPercent and pPercent to determine accuracy thresholds
  if(abs(pPercent-rPercent)<10)tokens += 10;
  else if(abs(pPercent-rPercent)<20)tokens+=8;
  else if(abs(pPercent-rPercent)<30)tokens+=6;
  else tokens+=4;
}

void startNotify(int totalTime, int flopfrq){
   notifyTimer = millis();
   endTimer = notifyTimer + totalTime;
   flop = false;
   flopFreq = flopfrq;
   duration = totalTime;
   digitalWrite(NOTIFIER_PIN, HIGH);
}

//Determines when and how to end the notify
void writeNotify(){
  Serial.println("Write notify");
  //Checks if there's a timer in progress, i.e. duration has been set.
  if(duration>0){
    //if the timer time has reached the flop time (the entire duration if 0 flopFreq)
    if(notifyTimer+(duration/(flopFreq+1))<=millis()){ 
        Serial.println("writing");     
        digitalWrite(NOTIFIER_PIN, flop?HIGH:LOW);
        flop = !flop;
        notifyTimer = millis();
        
    }
    //Deactivates timer if the time has been reached
    if(endTimer<=notifyTimer){
      duration = 0;
      digitalWrite(NOTIFIER_PIN, LOW);
    }
  }
}

//Allows notify system to go
void enableNotify(){
  canNotify = true;
}
//Prevents notify system from going off
void disableNotify(){
  digitalWrite(NOTIFIER_PIN, LOW);
  canNotify = false;
  duration = 0;
}
//Commits the persistent data to storage
void updateSave(){
  saveData newSave = {
    cycles,
    tokens
  };
  //Assumed same address
  EEPROM.put(0,newSave);
  
}

byte buttonInput() {
  //Register Button Hold Times
  //For that button, detect if it is held or released.
  //If the array elements are greater than 0, then that element has been held earlier.
  //Minimum time registered as any hold to avoid any flickering
  byte input = 0;

  //button 1 logic
  if (digitalRead(BUTTON1_PIN)) {
    //Determines if the signal duration has entered the hold range
    if (button_Times[0] >= minHoldTime)input = input | B1H_MASK;
    //While held, the button time will continue to increment
    button_Times[0] += delayRate;
  }
  else {
    //if the button hold duration is long enough to count as an intentional signal but not a hold, then it must be a press signal
    if (button_Times[0] >= minPressTime && button_Times[0] < minHoldTime)input = input | B1P_MASK;
    //Since the button has been 'released' or is currently not pressed, time is reset to 0.
    button_Times[0] = 0;
  }

  //Same logic as above but for button 2
  if (digitalRead(BUTTON2_PIN)) {
    //Determines if the signal duration has entered the hold range
    if (button_Times[1] >= minHoldTime)input = input | B2H_MASK;
    //While held, the button time will continue to increment
    button_Times[1] += delayRate;
  }
  else {
    //if the button hold duration is long enough to count as an intentional signal but not a hold, then it must be a press signal
    if (button_Times[1] >= minPressTime && button_Times[1] < minHoldTime)input = input | B2P_MASK;
    //Since the button has been 'released' or is currently not pressed, time is reset to 0.
    button_Times[1] = 0;
  }
  return input;
}

 

]]>
Double Transducer: Color to Linear Position https://courses.ideate.cmu.edu/60-223/f2019/work/double-transducer-color-to-linear-position/ Tue, 17 Sep 2019 02:26:10 +0000 https://courses.ideate.cmu.edu/60-223/f2019/work/?p=8094 Title: Double Transducer: Color to Linear Position

Overhead shot of the double transducer. The main components are labelled with names and numbers 1-3 which correspond to the order by which things are transduced.

Servo motor arm close-up. The black pin moves linearly along the track. The pieces were laser cut and attached using pins and glue.

The DIY color sensor without the color insert. RGB LEDs flash in sequence inside the 3D printed cover. The blue LED is on in this instance.

The working sequence. The arm extends when the color sensor detects red; once the red coin is removed, the arm retracts.

Description: A power source powers a color sensor, which sends a signal to generate a magnetic field when it detects a change in color. The presence of a magnetic field will in turn cause the motor to turn, moving an arm linearly along a track.

Progress:

Here we are testing the range of the servo motor arm and the distance this translates to along the track.

The failed first attempt at 3D printing a black-out cover for the color sensor. The tube was too small to cover the 3 LEDs and the photocell. We also chose red and yellow inserts for the final.

Wrapping copper wire to create the coils. This took some experimentation because we didn’t wrap around enough times in the first few attempts.

Discussion:

The makeshift RGB sensor worked surprisingly well after being wired up correctly, although the compact design increased the difficulty of properly wiring 3 LEDs and a photoresistor into the space of a little more than a square centimeter of space. The length of the legs of the LEDs were rather close, so at least one of the LEDs was inserted in the wrong direction initially. Despite minor problems, the RGB signal distinctly changed by about 100 for the red value after the red colored disk was inserted, which was a great success. However, it should be noted that the red signal was comparatively weaker, even with nothing else within the black casing, suggesting the possibility that the light from the previous LED isn’t given enough time to fade away. Admittedly, these were issues for the color sensor that weren’t predicted when designing the code and schematic from a purely theoretical perspective, but it did work as desired; this simple sensor exemplifies how valuable planning ahead was, yet it also reinforces how certain challenges might only be discovered in practice.

The solenoid system was neither simple or ended up working all too well. Numerous challenges arose, some from construction and others from human error. Although advice (thank you Zach for all the help with getting the solenoid circuits set up)  and research produced circuits that would ideally work well with more powerful solenoids,  the initial solenoid wire coils were initially only around a half-centimeter thick. So while the ‘drive coil’ (the coil that’s actively charged/discharged to produce a changing magnetic flux) could produce a tangible ‘pulse’ in a ceramic magnet disc held nearby, no signal could be detected in the receiving coil. After expanding the wire coils to a thickness of well over a centimeter, there still appeared to be no readable signal. This time it was due in part how the code, written beforehand, had used ‘digitalRead()’ instead of ‘analogRead()’, which made it impossible to detect the weak signal. Irregularities with how the Arduino was powered also interfered with consistent results. Overall, this experimental feature was a unique experience to test an unconventional signal transduction method and presented its fair share of challenges. Exercising good caution in design prevented any accidents from happening, though it might’ve been better to remove an existing coil from some motor or other component that was already wrapped and saved that time.

The linear motion servo made for little difficulty on the coding end since a library was available to easily interface with the device, requiring just a little calibration. Coming up with a design to convert its rotation to linear motion and constructing the largely mechanical instead of electrical component was another matter.

Schematic:

Code:

/* Project 1: Double Transducer

   Description: This code detects a color signal with a photoresistor and RGB LEDs, transmits that signal across two inductors,
   and passes out a 'positive' signal by driving a servo arm to extend a linear motion device.

   Resources Referenced:
   https://www.arduino.cc/reference/en/
   http://www.physics-and-radio-electronics.com/electronic-devices-and-circuits/rectifier/bridgerectifier.html
   https://create.arduino.cc/projecthub/mjrobot/arduino-color-detection-57e4ce?ref=tag&ref_id=color-detector&offset=0
   https://www.instructables.com/id/How-to-Control-an-Electromagnet-with-an-Arduino/

   Outputs
   Arduino Pin | Output
   2              Red LED
   3              Green LED
   4              Blue LED
   6              Output inductor Pin
   7              Servo Pin

   Input
   Arduino Pin | Input
   A0            Photoresistor
   A5            Input inductor Pin
*/

#include <Servo.h>

//Linear Motion
const int SERVO_PIN = 7; //OUTPUT, digital. Servo control pin.

const unsigned int rPeriod = 1000; //Parameter: refractory period for servo. Time for which a change to servo state cannot be changed again.

Servo arm; //Servo 'object'

bool moveConfirmed = false; //UTILITY: when true, arm extends at the end of the next refractory period, otherwise it'll retract
unsigned long lastmove = 0; //UTILITY: counter to determine time until refractory ends

//magnetic domain
const int O_MAG_PIN = 6; //OUTPUT, digital. Controls transistor powering the inductor.
const int I_MAG_PIN = A5; //INPUT, analog. Reads the read inductor

//magnetic domain: output
const unsigned long togThresh = 300; //PARAMETER, toggle output frequency in milliseconds. This determines how frequently the output inductor produces flux when it is permitted to do so.

unsigned long lasttoggle = 0; //UTILITY, toggle: Time since magnet signal was last toggled on/off. Only when the magnet is toggled
bool toggle = true; //UTILITY, toggle: used to determine which toggle step, on or off.

//magetic domain: input
const int thresh = 200; //UTILITY, minimum signal from the input pin from the inductor classified as a read
const int readTime = 200; //UTILITY, milliseconds taken by the entire read step
const int readFrq = 20; //UTILITY, number of times reads are taken in the

//Color Sensor
//(light detector)
const int PHOTO_PIN = A0; //INPUT, analog. Photoresistor pin. Signal corresponds to light level.
//(LED colors)
const int R_LED_PIN = 2; //OUTPUT, digital. RED LED pin, flashes first in series.
const int G_LED_PIN = 3; //OUTPUT, digital. GREEN LED pin, flashes second in series.
const int B_LED_PIN = 4; //OUTPUT, digital. BLUE LED pin, flashes last in series.

int target[3] = {300, 460, 230}; //PARAMETER for the RED  disk, must be calibrated based on lighting levels
//NOTE: Pervious testing values: casing has a color of about (195, 380, 295). RED side has about (270, 400, 250)

const int tolerance = 15; //UTILITY, +/- range that color matches target
int color[3]; //UTILITY, virtual RGB container variable representing the reflected color data

void setup() {
  // put your setup code here, to run once:
  //For debugging and viewing the inductor receiving signal
  Serial.begin(19200);

  //Linear Motion
  //OUTPUT
  arm.attach(SERVO_PIN);

  //Magnetic domain
  //INPUT
  pinMode(I_MAG_PIN, INPUT);
  //OUTPUT
  pinMode(O_MAG_PIN, OUTPUT); //To transistor


  //Color Sensor
  //INPUT
  pinMode(PHOTO_PIN, INPUT);
  //OUTPUT
  pinMode(R_LED_PIN, OUTPUT);
  pinMode(G_LED_PIN, OUTPUT);
  pinMode(B_LED_PIN, OUTPUT);

  delay(1000); //wait for the servo arm, as there may be cases where the signal is immediately present as soon as loop goes through
}

void loop() {
  // put your main code here, to run repeatedly:

  //Perform detection: check if RGB has changed enough to match some signal color
  colorRead(100); //Color detection; values stored in global array with a delay of 100 between each color.

  //Debugging line: Color input
  Serial.println((String)(color[0]) + " " + (String)(color[1]) + " " + (String)(color[2]));

  //if condition is met, power the inductor
  if (inThreshold()) {
    driveInductor();
  }

  //read the receivor inductor and confirm it has moved
  moveConfirmed = moveConfirmed || readInductor();

  //Attempts to move servo arm. Only proceeds if the refractory period has passed
  moveServoArm();
}

//move servo arm out and in
void moveServoArm() {
  //whenever the refractory period has passed, the arm is allowed to move again
  if (millis() - lastmove > rPeriod) {
    //moves arm when enough time has passed
    arm.write((moveConfirmed) ? 60 : 5); //If a move signal is measured at this step, move the arm out, otherwise pull it back in.
    lastmove = millis();
    moveConfirmed = false;
  }
}

//charge and discharge inductor to produce a varying flux
void driveInductor() {
  //if time since last toggle exceeds the threshold, toggle the inductor drive pin
  if (abs(millis() - lasttoggle) > togThresh) {
    digitalWrite(O_MAG_PIN, (toggle) ? HIGH : LOW);
    lasttoggle = millis();
    toggle = !toggle;
  }

}

//indicates whether there is substantial voltage induced from changing flux in the read inductor
bool readInductor() {
  //records if the signal ever exceeds a threshold
  bool successfulRead = false;
  for (int i = 0; i < readFrq; i++) {
    int fluxread = analogRead(I_MAG_PIN);
    //Debugging: Flux measurement results
    //Serial.println(fluxread);

    //checks if signal exceeds a threshold.
    successfulRead = successfulRead || fluxread > thresh;

    delay(readTime / readFrq); //divides the delay time into readFrq number of segments for a total of readTime worth of time

  }
  return successfulRead;
}

//Checks if light is within the threshold of being a target
bool inThreshold() {
  //loops for 1,2,3 or RGB to check all values are within tolerance threshold
  for (int i = 0; i < 3; i++) {
    if (abs(target[0] - color[0]) > tolerance)return false;
  }
  //if for no color it returns false, then all colors are in tolerance
  return true;
}

//Checks color by flashing each RGB LED color for reflection from chamber and sets value in color array
//frq variable used to control delay between reads, or the speed of the flashes.
//In theory, a longer delay would result in higher accuracy but sacrifice responsiveness and speed
void colorRead(int frq) {
  setLED(0); //reset to no LEDs
  setLED(1); //RED LED ON
  delay(frq);
  color[0] = analogRead(PHOTO_PIN); //read RED
  setLED(0);
  setLED(2); //GREEN LED ON
  delay(frq);
  color[1] = analogRead(PHOTO_PIN); //read GREEN
  setLED(0);
  setLED(3); //BLUE LED ON
  delay(frq);
  color[2] = analogRead(PHOTO_PIN); //read BLUE
  setLED(0);
}

//Sets the LEDs on/off based on a key value. A switch case might work even better, but this function is still perfectly functional.
void setLED(int key) {
  if (key == 0) {
    digitalWrite(R_LED_PIN, LOW);
    digitalWrite(G_LED_PIN, LOW);
    digitalWrite(B_LED_PIN, LOW);
  }
  else if (key == 1)digitalWrite(R_LED_PIN, HIGH);
  //else digitalWrite(R_LED_PIN,LOW);
  else if (key == 2)digitalWrite(G_LED_PIN, HIGH);
  //else digitalWrite(G_LED_PIN,LOW);
  else if (key == 3)digitalWrite(B_LED_PIN, HIGH);
  //else digitalWrite(B_LED_PIN,LOW);
}

 

]]>
Bluetooth Controlled Arduino LED Table https://courses.ideate.cmu.edu/60-223/f2019/work/bluetooth-controlled-arduino-led-table/ Tue, 27 Aug 2019 21:21:23 +0000 https://courses.ideate.cmu.edu/60-223/f2019/work/?p=8003

Name: Bluetooth Controlled Arduino LED Table

Project Creator: MarkQ8

Short Description: This LED coffee table was the creator’s first Arduino project, intended to serve as a “cool” light source for a room as well as a simple gaming platform that could be controlled via Bluetooth, which allowed it to be controlled by a mobile device.

LED strips visible within table

Response: I think it’s a great way to add more functionality to a simple coffee table. Plus, who wouldn’t want to show off their new gaming ‘console’?

Improvements: Personally, I think these tables would be great in some themed cafe, where they could be paired together with another table, so people could play battleship or other simple multiplayer games with friends or strangers. Nothing brings people together like sinking each other’s battleships.

]]>