Homework 7: Scorekeeping with timing and events
Flexible due date: Tuesday, Dec. 1st or Thursday, Dec. 3rd
In basketball, it’s possible to score one, two, or three points at a time. You and your friends want an easy scorekeeping system which is designed to tally the number of points that your team has.
“If the other team sees this awesome system,” you think, “they will be so intimidated by how clever we are at building things that they’ll become terrible at basketball.”
It’s a foolproof plan.
Assignment
Your clever scoring system will consist of four buttons (described below), three indicator LEDs, and Serial
feedback that specifies the Arduino’s internal timer, the last type of basket made, and the current score.
The buttons should be wired using the INPUT_PULLUP
pin mode (which will save you a resistor). Their roles are:
- The one point button. When pushed, add one point to the score, announce
millis = xxxxx, free throw scored, x points
and light a green LED ⬤ for two seconds. - The two point button. When pushed, add two points to the score, announce
millis = xxxxx, field goal scored, x points
and light a yellow LED ⬤ for two seconds. - The three point button. When pushed, add three points to the score, announce
millis = xxxxx, three pointer scored, x points
and light a red LED ⬤ for two seconds. - The reset button. This button has a safety feature so you don't trigger a reset accidentally during a game: if the button is not held down for at least one second, it has no effect. As soon as it has been held down continuously for one second, announce
millis = xxxxx, ----- game reset -----, 0 points
and change the score to zero. (Holding the button down for longer than one second should not reset the system repeatedly.)
Example
Here is an example interaction, to illustrate the intended outcome: shortly after the Arduino turns on, the user holds down the “reset” button for a bit longer than one second, and the Serial
feedback reads:
millis = 2492, ----- game reset -----, 0 points
Then, about 10 seconds after the Arduino turns on, the user pushes the three point button. The red LED lights for two seconds, and the Arduino sends a new line of text so that the complete Serial
feedback now reads:
millis = 2492, ----- game reset -----, 0 points millis = 10284, three pointer scored, 3 points
→ Note that Serial
feedback is only sent when a button is pushed. It is not sent once per second, etc. ←
If, about 20 seconds after the Arduino was turned on, the user were to then push the two point button: the yellow LED would illuminate for one second, and the Arduino would send one more line of text, so the complete Serial
feedback would read:
millis = 2492, ----- game reset -----, 0 points millis = 10284, three pointer scored, 3 points millis = 20625, field goal scored, 5 points
A final important note: each button’s LED is supposed to light for two seconds after that button was pushed—but during that time, it’s important that the board is still able to receive a new button push on that, or any different button. This means that you can’t simply use a delay()
to keep that LED on—you’ve got to use the chip’s on-board timer, called millis()
, to help you out.
Deliverables
Upload your deliverables to this homework’s Canvas assignment.
You may choose to build this assignment in simulation (via Tinkercad), or using your actual physical Arduino board. I prefer that you actually build it out physically, in part because Tinkercad will not simulate “bounce” (which is a real problem you’ll really face in the world if you build things with buttons in them), and in part because I’d like you to have one more chance to dust off those physical circuit-building skills before the semester’s over.
Particular deliverable for students physically building the board (preferred mode)
Please shoot a quick(ish) video with the following things in the following order. You can do it all in one take! (No need to edit separate clips together.) Upload this video to the Canvas assignment.
- Press any scoring button so I can see the associated LED until it turns off.
- Show the serial feedback on your computer screen.
- Press a few buttons in rapid succession (for instance, press the 1-point button, then the 2-point button, then the 3-point, without waiting in between) so I can see the associated LEDs until they turn off.
- Show the serial feedback on your computer screen.
- Press the reset button briefly (the score should not reset).
- Show the serial feedback on your screen. (This way I know the score didn’t reset.)
- Hold the reset button for three seconds (the score should reset once).
- Show the serial feedback on the screen. (The score should be shown as having reset.)
- Have fun with your machine! Press lots of buttons or whatever. Ideally, narrate a sweet basketball game, with a thrilling conclusion. Silliness is encouraged!
Particular deliverable for students using Tinkercad
Share a link to your Tinkercad Circuit which allows me to simulate and test the circuit: add this link to the circuit at the top of your code in the comment block, as shown below.
To create your the link, in the upper right corner of the Tinkercad window, click Share, then Invite People, then copy the generated link.
Deliverables for everyone, regardless of how you build the electronics
- A schematic drawing of the machine’s wiring, either on paper and then scanned/photographed, or drawn on a computer. CircuitLab makes things easy!
- Code submission; your file name should have the form
andrewID-hw7.ino
.
Be sure to include a course-standard comment block at the top of the code you submit (including the Tinkercad Circuit link, if you used Tinkercad):
/*
* Homework 3
* Andrew Carnegie (acarnegi)
* 1 hour 25 minutes
*
* Tinkercad Circuit: https://www.tinkercad.com/things/irHFPHYpxJ0-simple-blinker/editel?sharecode=g0YqjUddc0IvqzoDNJzHahaCQSNVB-7sB5M6bea0mls=
*
* Collaboration: Ada Lovelace helped me understand
* variables and we worked on the code together.
* The function "Wheel" is copied from this Adafruit
* tutorial: https://learn.adafruit.com/multi-tasking-the-arduino-part-3/utility-functions
*
* Challenge: I tried to write a switch…case but
* it did not go as planned. It took me an extra hour!
* I was held up with bracket trouble the whole time,
* it seems.
*
* Next time: I'll start by writing comments or pseudo-code
* before trying to write the actual code. I think this
* will reduce my getting tangled around the code
* structure.
*
* Description: The code below blinks three LEDs according
* to a pattern. Pushing a button selects the next pattern
* from a pre-defined list.
*
* Pin mapping:
*
* pin | mode | description
* ------|--------|------------
* 4 input momentary pushbutton for mode selection
* 9 output red LED
* 10 output green LED
* 12 output yellow LED
*
*/
Use good code practice:
- Select good names for variables, including
camelCase
names for variables which will change values andALLCAPS
for those which won’t - Use the
const
qualifier for any variable that won’t change, such as pin assignments, e.g.const int BUTTONPIN = 7
or other constants, e.g.const float PI = 3.1416
. - Add comments as needed to help explain non-obvious things to any future readers
Collaboration
Limited collaboration with your colleagues is permitted. You must build your own circuit and write your own code, but you can talk with other students to get advice and reason through the problem together. You are not permitted to copy anybody else’s code (whether that person is in this class or not). You are permitted to use any internet/book/etc. resource you’d like for guidance, so long as you do not copy code verbatim from such a source. Be sure to list your sources in the “Collaboration” section of your comment block.
Feedback
points | assignment |
---|---|
1 | inputs wired properly (and using INPUT_PULLUP configuration) |
1 | outputs wired properly (with current-limiting resistors) |
4 | button and LED timing working properly |
5 | serial feedback (working as described above, and formatted properly) |
3 | schematic (legible, clean, and valid) |
6 | code written cleanly, clearly, and with appropriate commenting practice |
20 | total |
A millis()
reminder
The Arduino has a built-in timer that begins counting as soon as the board gets power. It counts up in milliseconds, which means the number gets pretty big, pretty quickly. This timer is not affected by a delay()
statement or any other thing going on in your code—it’s a very reliable value.
To find out precisely how many milliseconds it’s been since the chip turned on, you simply run the built-in function millis()
, which will return the answer. For instance, if you want to find out where millis()
is at this moment, simply run
Serial.println(millis());
The power of millis()
is that by referring to this active timer, you can make things happen on a schedule without using delay()
statements. This is a big deal! It means that multiple tasks can run concurrently on their own schedules, without interfering with each other.
See the Blink without blocking section of Code bites for some implementation details.
Using millis()
to run a one-second timer: the event-loop model
Here is some minimalistic code which outlines in short form how to use millis()
to time a task:
unsigned long timer = 0;
void setup() {}
void loop() {
// something I want to run every second
if (millis() - timer >= 1000){ // greater than or equal!
// this happens once per second
timer = millis();
}
}
Help! My buttons keep pressing!
Why do your buttons keep pressing over and over? Because once you’ve gotten rid of the delay()
s in your code, those button-press detections are able to run over and over as the loop()
keeps executing repeatedly. Part of the challenge of this assignment is solving this problem.
You’ll need to debounce your button presses. There’s a tutorial on that topic in the left navigation bar featuring example code, but here’s a one-sentence clue of how you might want to approach the problem: instead of detecting the current button state on each loop, detect the state change from unpressed to pressed.
Hints:
- Use some global state variables, perhaps like this:
bool buttonState = true; bool lastButtonState = true;
- Use these variables in your loop to detect when a button transitions from unpressed to pressed
- Be careful to update the variables at the appropriate times
- Recall that
true
andHIGH
are synonymous, andfalse
andLOW
are as well. (This is somewhat imprecise speech, but it basically holds true in Arduinoland.) - Remember that when you’re using
INPUT_PULLUP
mode, a button will beHIGH
until it’s pressed