8044ebab4470d69349248847f7d9cd2a5f7cc3d7
haas/fall2026/c4eng/projects/cat0.md
| ... | ... | @@ -0,0 +1,443 @@ |
| 1 | +# ENGR1050 C for Engineers |
|
| 2 | + |
|
| 3 | +# PROJECT: Create A Theremin (CAT0) |
|
| 4 | + |
|
| 5 | +## OBJECTIVE |
|
| 6 | + |
|
| 7 | +Before we can make an instrument that plays by waving your hand, we need |
|
| 8 | +to understand its two parts. We commence our exploration of two new |
|
| 9 | +components from the electronics kit: an ultrasonic distance sensor that |
|
| 10 | +measures how far away things are, and a passive buzzer that makes the |
|
| 11 | +sounds we tell it to. |
|
| 12 | + |
|
| 13 | +## PROCESS |
|
| 14 | + |
|
| 15 | +Do note, the productive way to go about this project involves taking the |
|
| 16 | +following steps: |
|
| 17 | + |
|
| 18 | + * starting early |
|
| 19 | + * reading the project page |
|
| 20 | + * asking questions regarding things you do not know, are not clear on, |
|
| 21 | + or are confused about |
|
| 22 | + * as information, concepts, processes become clear, that is something |
|
| 23 | + you can contribute to the project documentation (so you can better |
|
| 24 | + remember) |
|
| 25 | + |
|
| 26 | +If you start too late, and do not ask questions, and do not have enough |
|
| 27 | +time and don't know what is going on, you are not doing the project |
|
| 28 | +correctly. |
|
| 29 | + |
|
| 30 | +## OBTAIN THE PROJECT FILES |
|
| 31 | + |
|
| 32 | +To assist with consistency across all implementations, data files for use |
|
| 33 | +with this project are available on lab46 as a `grabit`. Please copy the |
|
| 34 | +data to your private semester repository under an appropriate project |
|
| 35 | +directory to begin work, and ensure your implementation properly works |
|
| 36 | +with the provided data. |
|
| 37 | + |
|
| 38 | +The project should be obtained by the **Sunday prior to the due date**. |
|
| 39 | + |
|
| 40 | +``` |
|
| 41 | +lab46:~/SEMESTER/c4eng$ grabit c4eng cat0 |
|
| 42 | +``` |
|
| 43 | + |
|
| 44 | +Please study any provided code and supporting documents, and look up, |
|
| 45 | +experiment, and ask questions on aspects that you do not understand. |
|
| 46 | + |
|
| 47 | +## OPERATING THE PROJECT |
|
| 48 | + |
|
| 49 | +These projects will have two paths to them, depending on which device you |
|
| 50 | +are using: |
|
| 51 | + |
|
| 52 | + * Raspberry Pi single board computer (4/5) using the `wiringPi` library |
|
| 53 | + * Raspberry Pi Pico microcontroller |
|
| 54 | + |
|
| 55 | +``` |
|
| 56 | +lab46:~/SEMESTER/c4eng/cat0$ make |
|
| 57 | +Please select which development environment: |
|
| 58 | + |
|
| 59 | + make wiringpi - using the Raspberry Pi with wiringpi library |
|
| 60 | + make pico - using the Raspberry Pi pico |
|
| 61 | + |
|
| 62 | +after which, you can use 'make' to build |
|
| 63 | +lab46:~/SEMESTER/c4eng/cat0$ |
|
| 64 | +``` |
|
| 65 | + |
|
| 66 | +Run the `make` command with the category representing the path you will |
|
| 67 | +be working with for this project. |
|
| 68 | + |
|
| 69 | +After which, if you do an `ls`, you will see a cyan-colored file called |
|
| 70 | +`cat0.c`. This is the C file you will be editing for this project. |
|
| 71 | + |
|
| 72 | +Go ahead and view it, study the code, eventually make any changes, save |
|
| 73 | +and return to the command-line. |
|
| 74 | + |
|
| 75 | +To build it, you simply run: `make` |
|
| 76 | + |
|
| 77 | +If there are no errors, it will build, and you proceed from there. |
|
| 78 | + |
|
| 79 | +## MAINTAINING YOUR REPOSITORY |
|
| 80 | + |
|
| 81 | +Be sure to keep your repository up-to-date, regularly committing changes |
|
| 82 | +to files. |
|
| 83 | + |
|
| 84 | +As a reminder, here are the suggested steps: |
|
| 85 | + |
|
| 86 | + -1. `make clean` <-- remove all unnecessary files |
|
| 87 | + 0. `git status` <-- see the current state of things |
|
| 88 | + 1. `git pull` <-- pull down any changes from elsewhere |
|
| 89 | + 2. `git add` <-- add each new or modified file |
|
| 90 | + 2a. `git add ./` <-- try this the first time |
|
| 91 | + 3. `git commit -m "your message"` <-- make a snapshot |
|
| 92 | + 4. `git push` |
|
| 93 | + |
|
| 94 | +## UNDERSTANDING THE TECHNOLOGY |
|
| 95 | + |
|
| 96 | +### The ultrasonic sensor (HC-SR04) |
|
| 97 | + |
|
| 98 | +The sensor has two "eyes": one is a speaker that emits a burst of |
|
| 99 | +ultrasound (too high-pitched for us to hear), and the other is a |
|
| 100 | +microphone listening for that burst to bounce back. It has four pins: |
|
| 101 | + |
|
| 102 | + * **VCC** and **GND**: power (the sensor runs on 5 volts) |
|
| 103 | + * **Trig** (trigger): an input to the sensor. A HIGH pulse here, at |
|
| 104 | + least 10 microseconds long, tells the sensor to take a measurement. |
|
| 105 | + * **Echo**: an output from the sensor. It goes HIGH when the burst is |
|
| 106 | + sent, and goes LOW when the echo returns. |
|
| 107 | + |
|
| 108 | +The sensor in our kit is rated for distances of 2cm to 200cm. |
|
| 109 | + |
|
| 110 | +Note what the sensor does NOT do: it does not tell us a distance. It |
|
| 111 | +gives us a **time**: the length of time Echo stayed HIGH. Turning that |
|
| 112 | +time into a distance is our job (and our math). Sound travels at about |
|
| 113 | +343 meters per second, which is 0.0343 centimeters per microsecond. Since |
|
| 114 | +the time measured covers the trip to the object AND back, we only want |
|
| 115 | +half of it: |
|
| 116 | + |
|
| 117 | +``` |
|
| 118 | +distance (cm) = time (us) * 0.0343 / 2 which is about time (us) / 58 |
|
| 119 | +``` |
|
| 120 | + |
|
| 121 | +Check your understanding: if Echo stays HIGH for 1160 microseconds, how |
|
| 122 | +far away is the object? |
|
| 123 | + |
|
| 124 | +Timing a pulse this short means our program sits in a tight loop, reading |
|
| 125 | +the Echo pin as fast as it can, noting the time when it changes. If Echo |
|
| 126 | +never changes (nothing in range, a wire fell out), a naive loop would |
|
| 127 | +wait forever, so the code you are given gives up after a while and |
|
| 128 | +reports that no echo was received. Your code needs to handle that case. |
|
| 129 | + |
|
| 130 | +Sensors are not perfect. Readings wobble. Soft surfaces (clothing, foam) |
|
| 131 | +absorb sound; angled surfaces bounce it off in the wrong direction. You |
|
| 132 | +will see this for yourself. |
|
| 133 | + |
|
| 134 | +### The passive buzzer |
|
| 135 | + |
|
| 136 | +Our kit contains TWO kinds of buzzer, and they look similar: |
|
| 137 | + |
|
| 138 | + * an **active** buzzer contains its own oscillator. Apply power, and |
|
| 139 | + it makes sound. Remove power, and it stops. Its pitch is built in: |
|
| 140 | + we can only turn it ON or OFF. |
|
| 141 | + * a **passive** buzzer is just a speaker element: it makes sound only |
|
| 142 | + when we feed it a rapidly switching signal, and the pitch it makes |
|
| 143 | + is whatever frequency we feed it. |
|
| 144 | + |
|
| 145 | +We need to be able to change the pitch, so **we want the passive |
|
| 146 | +buzzer**. To tell them apart: |
|
| 147 | + |
|
| 148 | + * an active buzzer often has a label covering the hole where the |
|
| 149 | + sound comes out (though not always) |
|
| 150 | + * turn them over: the bottom of an active buzzer is sealed (it hides a |
|
| 151 | + circuit and a crystal), while the bottom of a passive buzzer is open, |
|
| 152 | + and you can see a circuit board, coils, and a magnet through the pin |
|
| 153 | + holes |
|
| 154 | + |
|
| 155 | +If you use the wrong buzzer you will find out quickly: an active buzzer |
|
| 156 | +will make the same tone no matter what frequency we ask for, and a |
|
| 157 | +passive buzzer given steady power will just click. |
|
| 158 | + |
|
| 159 | +**Frequency** is how many times per second something repeats, measured in |
|
| 160 | +Hertz (Hz). For sound, frequency is pitch: the higher the frequency, the |
|
| 161 | +higher the note. The concert A that orchestras tune to (the A above |
|
| 162 | +middle C) is 440Hz. Doubling a frequency raises the note by an |
|
| 163 | +**octave**. Dividing an octave into twelve equal steps (the twelve |
|
| 164 | +semitones of the piano keyboard) means each step multiplies the frequency |
|
| 165 | +by the twelfth root of 2 (about 1.0595). |
|
| 166 | + |
|
| 167 | +The passive buzzer in our kit has a resonant frequency of 2000Hz, meaning |
|
| 168 | +it is loudest there. Notes far from 2000Hz will sound weaker. |
|
| 169 | + |
|
| 170 | +On the Raspberry Pi, the `wiringPi` library provides `softTone` |
|
| 171 | +functionality: `softToneCreate()` prepares a pin, and `softToneWrite()` |
|
| 172 | +sets the frequency (0 for silence) while a background thread does the |
|
| 173 | +rapid switching for us. On the pico, we use its PWM hardware to produce |
|
| 174 | +the switching, and the code provided has `tone_setup()` and `tone_play()` |
|
| 175 | +functions to do so (study them!). |
|
| 176 | + |
|
| 177 | +## TASK |
|
| 178 | + |
|
| 179 | +Taking our knowledge of GPIOs, LEDs, and now the ultrasonic distance |
|
| 180 | +sensor and the passive buzzer: |
|
| 181 | + |
|
| 182 | + * wire up the ultrasonic distance sensor (two unique GPIO pins: one |
|
| 183 | + for trigger, one for echo) |
|
| 184 | + * wire up the passive buzzer to its own unique GPIO pin |
|
| 185 | + * get the provided code working: it plays a test tone, then measures |
|
| 186 | + the distance continuously, flagging when an object is within a |
|
| 187 | + threshold distance. On the Raspberry Pi it reports the distance on |
|
| 188 | + the screen. The pico has no screen, so it lights its onboard LED |
|
| 189 | + whenever an object is within the threshold |
|
| 190 | + * extend the code as described below |
|
| 191 | + |
|
| 192 | + |
|
| 193 | + |
|
| 194 | +### The circuit |
|
| 195 | + |
|
| 196 | +You will be building two small circuits (they are the same circuits found |
|
| 197 | +in the Freenove tutorial). Pin choices are defined at the top of your |
|
| 198 | +`cat0.c` file; if you wire to different pins, change the values there. |
|
| 199 | + |
|
| 200 | +**Ultrasonic sensor** |
|
| 201 | + |
|
| 202 | +| Sensor pin | Raspberry Pi | Pico | |
|
| 203 | +| ---------- | ------------------------------- | -------------------------- | |
|
| 204 | +| VCC | 5V (physical pin 2) | VBUS (physical pin 40) | |
|
| 205 | +| GND | GND (physical pin 6) | GND (physical pin 38) | |
|
| 206 | +| Trig | wiringPi 4 (GPIO23, pin 16) | GP2 (physical pin 4) | |
|
| 207 | +| Echo | wiringPi 5 (GPIO24, pin 18) | GP3 (physical pin 5) | |
|
| 208 | + |
|
| 209 | +**IMPORTANT: Echo must go through a voltage divider.** The sensor is |
|
| 210 | +powered by 5 volts, so its Echo output is a 5 volt signal. The GPIO pins |
|
| 211 | +on both the Raspberry Pi and the pico are 3.3 volt devices, and are NOT 5 |
|
| 212 | +volt tolerant: connecting Echo directly can damage the pin (or the whole |
|
| 213 | +board). Three 1k ohm resistors fix this: |
|
| 214 | + |
|
| 215 | +``` |
|
| 216 | + Echo ----[ 1k ohm ]----+---- to GPIO (wiringPi 5 / GP3) |
|
| 217 | + | |
|
| 218 | + +----[ 1k ohm ]----[ 1k ohm ]---- GND |
|
| 219 | +``` |
|
| 220 | + |
|
| 221 | +The resistors split the 5 volts in a 1:2 ratio, leaving 5 * 2/3, which is |
|
| 222 | +about 3.3 volts at the GPIO. Trig is fine connected directly: the GPIO is |
|
| 223 | +sending 3.3 volts to the sensor, which the sensor understands. |
|
| 224 | + |
|
| 225 | +**Passive buzzer** |
|
| 226 | + |
|
| 227 | +The buzzer draws more current than a GPIO pin should supply, so we have |
|
| 228 | +the GPIO control a transistor, which in turn switches the buzzer: |
|
| 229 | + |
|
| 230 | +| Connection | Goes to | |
|
| 231 | +| ------------------------------------------------- | ---------------------- | |
|
| 232 | +| GPIO (Pi: wiringPi 0 / pico: GP4, physical pin 6) | 1k ohm resistor | |
|
| 233 | +| other end of 1k ohm resistor | transistor base | |
|
| 234 | +| transistor emitter | GND | |
|
| 235 | +| transistor collector | buzzer negative (-) | |
|
| 236 | +| buzzer positive (+) | 5V | |
|
| 237 | + |
|
| 238 | +For the Raspberry Pi, wiringPi 0 is GPIO17 (physical pin 11), and 5V is |
|
| 239 | +physical pin 2 (or 4). For the pico, 5V is VBUS (physical pin 40). The |
|
| 240 | +GPIO only ever touches the 1k ohm resistor and the transistor base: the 5 |
|
| 241 | +volts goes no further than the buzzer and the transistor's collector. |
|
| 242 | + |
|
| 243 | +The transistor in the kit is an NPN type (S8050). Make sure you have the |
|
| 244 | +correct leg for each of the emitter, base, and collector: with the flat |
|
| 245 | +side facing you and the legs pointing down, they are (left to right) |
|
| 246 | +**E**mitter, **B**ase, **C**ollector. If the buzzer never sounds, or |
|
| 247 | +sounds constantly, check the orientation and your connections! |
|
| 248 | + |
|
| 249 | +### The extensions |
|
| 250 | + |
|
| 251 | +The code you are given works, but it has some deliberate gaps, and what |
|
| 252 | +you fill in here you will need in the next project (where we turn this |
|
| 253 | +into an instrument). Read all of the provided code carefully, and make |
|
| 254 | +sure you understand every line: you may be asked to explain it. |
|
| 255 | + |
|
| 256 | +You are to: |
|
| 257 | + |
|
| 258 | +**1. Calibrate your sensor.** Use a ruler or tape measure to place an |
|
| 259 | +object (a book, a flat piece of cardboard) at known distances in front of |
|
| 260 | +the sensor, and compare what the sensor reports. Fill in the calibration |
|
| 261 | +table in the comments at the top of the source file with readings at 10, |
|
| 262 | +25, 50, 100, and 150cm, and notes on what you notice: how far off is it? |
|
| 263 | +Does the reading jump around? What happens closer than 2cm? What happens |
|
| 264 | +with a soft object (try your sleeve) or an angled one? |
|
| 265 | + |
|
| 266 | +(The pico has no screen, so the only thing it can tell you is whether the |
|
| 267 | +distance is within `THRESHOLD`, by lighting its LED. To find what it |
|
| 268 | +believes the distance to be, find the smallest `THRESHOLD` that lights |
|
| 269 | +the LED with your object at each ruler distance. The comments in the |
|
| 270 | +source file explain.) |
|
| 271 | + |
|
| 272 | +**2. Finish `play_note()`.** This function is given a frequency (in |
|
| 273 | +Hertz) and a duration (in milliseconds), and sounds that note for that |
|
| 274 | +duration. As provided, it starts the note, and waits... but never ends |
|
| 275 | +it: after the first note, the buzzer just keeps sounding. A note should |
|
| 276 | +be followed by silence (for `NOTEGAP` milliseconds), so that notes played |
|
| 277 | +back to back are distinct from each other, and so that the buzzer is |
|
| 278 | +quiet when we are done. Study the function, and complete it. |
|
| 279 | + |
|
| 280 | +**3. Play a scale.** Create an **array** holding the frequencies of the C |
|
| 281 | +major scale, from the C just below the A of 440Hz up to the C an octave |
|
| 282 | +above it (eight notes), and use a loop and your `play_note()` to play |
|
| 283 | +each note in turn at startup, in place of the single test note. Do not |
|
| 284 | +write eight separate `play_note()` calls! |
|
| 285 | + |
|
| 286 | +Some guidance: |
|
| 287 | + |
|
| 288 | + * the notes of a major scale are spaced (in semitones) whole step, |
|
| 289 | + whole step, half step, whole step, whole step, whole step, half step. |
|
| 290 | + A whole step is two semitones, a half step is one |
|
| 291 | + * you may hard-code the frequencies in your array, or calculate them |
|
| 292 | + (from 440Hz and the semitone relationship above, with the `pow()` |
|
| 293 | + function from `math.h`). If you hard-code them, cite where you |
|
| 294 | + found them. Either way, they should sound like a scale! |
|
| 295 | + |
|
| 296 | +**4. Finish `sample_distance()`.** A single reading can be noisy, or fail |
|
| 297 | +altogether, so this function takes several (`SAMPLES` of them), stores |
|
| 298 | +the good ones in an **array**, and returns the **median** of them: the |
|
| 299 | +middle value, after sorting them from smallest to largest. As provided, |
|
| 300 | +it does nearly everything: it takes the readings, keeps only the |
|
| 301 | +successful ones, reports no reading if every one of them failed, and |
|
| 302 | +returns the middle value of the array. But the values are never sorted, |
|
| 303 | +so what it returns is simply the third reading, not the median! |
|
| 304 | + |
|
| 305 | +The loops that perform a "bubble sort" are provided; what is missing is |
|
| 306 | +what goes inside them: the comparison and the swap. Complete it. Then: |
|
| 307 | + |
|
| 308 | + * if you end up with an even number of good readings, there are two |
|
| 309 | + middle values. The provided function returns the upper one. Decide |
|
| 310 | + if that is what you want (or if you would rather average the two) |
|
| 311 | + and document your decision in a comment |
|
| 312 | + * test it: move an object in and out of range, wave your hand across |
|
| 313 | + the sensor, and observe how steadily it behaves compared to a single |
|
| 314 | + reading (on the Pi, watch the reported distance; on the pico, watch |
|
| 315 | + the LED when your object sits right around `THRESHOLD`) |
|
| 316 | + |
|
| 317 | +As you do this, consider the trade-off the constants in this function |
|
| 318 | +represent. What does taking more samples do for the smoothness of your |
|
| 319 | +readings? What does it do for how quickly your program notices that |
|
| 320 | +something has moved? Experiment with `SAMPLES` and `SAMPLEGAP`. We will |
|
| 321 | +care about this a great deal in the next project. |
|
| 322 | + |
|
| 323 | +## EXECUTION |
|
| 324 | + |
|
| 325 | +If on the pico, follow the process for downloading the binary file and |
|
| 326 | +programming your pico. The pico has no screen: its onboard LED is how |
|
| 327 | +it tells you what the sensor sees (lit means something is within |
|
| 328 | +`THRESHOLD`), and the buzzer is how you know it is alive. |
|
| 329 | + |
|
| 330 | +If on the full Raspberry Pi, you have a green executable you can run just |
|
| 331 | +as we've done in our other projects. |
|
| 332 | + |
|
| 333 | +## STRATEGY |
|
| 334 | + |
|
| 335 | +The general flow of the process (one way of going about it, anyway) can |
|
| 336 | +be described as follows: |
|
| 337 | + |
|
| 338 | +``` |
|
| 339 | +INITIALIZE THE GPIOS AND THE BUZZER |
|
| 340 | +FOR EACH NOTE IN THE SCALE ARRAY: |
|
| 341 | + PLAY THE NOTE FOR A MOMENT |
|
| 342 | + |
|
| 343 | +REPEAT INFINITELY: |
|
| 344 | + OBTAIN THE MEDIAN DISTANCE |
|
| 345 | + REPORT THE DISTANCE (ON THE PI), OR THAT THERE WAS NO READING |
|
| 346 | + SHOULD AN OBJECT BE WITHIN THE THRESHOLD: |
|
| 347 | + FLAG IT (LIGHT THE LED ON THE PICO) |
|
| 348 | + DELAY BRIEFLY |
|
| 349 | + |
|
| 350 | +REPEAT BLOCK |
|
| 351 | +``` |
|
| 352 | + |
|
| 353 | +And for the median function (the parts provided, and the parts you |
|
| 354 | +finish): |
|
| 355 | + |
|
| 356 | +``` |
|
| 357 | +GIVEN NOTHING (THE SENSOR IS ALREADY SET UP): |
|
| 358 | + REPEAT FOR EACH OF THE SAMPLES TO BE TAKEN: |
|
| 359 | + MEASURE THE DISTANCE |
|
| 360 | + SHOULD THE MEASUREMENT HAVE SUCCEEDED: |
|
| 361 | + STORE IT IN THE NEXT FREE SPOT IN THE ARRAY |
|
| 362 | + PAUSE FOR THE SENSOR TO SETTLE |
|
| 363 | + |
|
| 364 | + SHOULD NO MEASUREMENTS HAVE SUCCEEDED: |
|
| 365 | + RETURN NO READING |
|
| 366 | + |
|
| 367 | + SORT THE VALUES THAT WERE STORED |
|
| 368 | + RETURN THE MIDDLE ONE |
|
| 369 | +``` |
|
| 370 | + |
|
| 371 | +## SUBMISSION |
|
| 372 | + |
|
| 373 | +To be successful in this project, the following criteria (or their |
|
| 374 | +equivalent) must be met: |
|
| 375 | + |
|
| 376 | + * Project must be submit on time, by the deadline. |
|
| 377 | + * Late submissions will lose 33% credit per day, with the submission |
|
| 378 | + window closing on the 3rd day following the deadline. |
|
| 379 | + * All code must compile cleanly (no warnings or errors) |
|
| 380 | + * Compile with the `-Wall` and `--std=gnu18` compiler flags |
|
| 381 | + * All requested functionality must conform to stated requirements |
|
| 382 | + (either in this document or in a comment banner in the source code |
|
| 383 | + files themselves). |
|
| 384 | + * Executed programs must display in a manner similar to provided output |
|
| 385 | + * Output formatted, where applicable, must match that of project |
|
| 386 | + requirements |
|
| 387 | + * Processing must be correct based on input given and output requested |
|
| 388 | + * Output, if applicable, must be correct based on values input |
|
| 389 | + * Code must be nicely and consistently indented |
|
| 390 | + * Code must be consistently written, to strive for readability from |
|
| 391 | + having a consistent style throughout |
|
| 392 | + * Code must be commented |
|
| 393 | + * Any "to be implemented" comments **MUST** be removed |
|
| 394 | + * These "to be implemented" comments, if still present at |
|
| 395 | + evaluation time, will result in points being deducted. |
|
| 396 | + * Sufficient comments explaining the point of provided logic **MUST** |
|
| 397 | + be present |
|
| 398 | + * No global variables (without instructor approval), no goto |
|
| 399 | + statements, no calling of main()! |
|
| 400 | + * Track/version the source code in your lab46 semester repository |
|
| 401 | + * Submit a copy of your source code to me using the `submit` tool |
|
| 402 | + (`make submit` on lab46 will do this) by the deadline. |
|
| 403 | + |
|
| 404 | +### Submit Tool Usage |
|
| 405 | + |
|
| 406 | +Let's say you have completed work on the project, and are ready to |
|
| 407 | +submit, you would do the following: |
|
| 408 | + |
|
| 409 | +``` |
|
| 410 | +lab46:~/SEMESTER/c4eng/cat0$ make submit |
|
| 411 | +``` |
|
| 412 | + |
|
| 413 | +You should get some sort of confirmation indicating successful submission |
|
| 414 | +if all went according to plan. If not, check for typos and or locational |
|
| 415 | +mismatches. |
|
| 416 | + |
|
| 417 | +What I'll be looking for: |
|
| 418 | + |
|
| 419 | +### RUBRIC |
|
| 420 | + |
|
| 421 | +I'll be evaluating the project based on the following criteria: |
|
| 422 | + |
|
| 423 | +``` |
|
| 424 | +286:cat0:final tally of results (286/286) |
|
| 425 | +*:cat0:used grabit to obtain project by the Sunday prior to duedate [13/13] |
|
| 426 | +*:cat0:post a short video to the class DISCORD [26/26] |
|
| 427 | +*:cat0:ultrasonic sensor trigger and echo hooked to unique GPIOs [26/26] |
|
| 428 | +*:cat0:passive buzzer hooked to its own unique GPIO [13/13] |
|
| 429 | +*:cat0:distance correctly measured and reported (pico: via LED) [26/26] |
|
| 430 | +*:cat0:calibration table completed, with observations [39/39] |
|
| 431 | +*:cat0:no echo, out of range is handled (no hang, no bogus values) [13/13] |
|
| 432 | +*:cat0:play_note() completed: each note ends, followed by short silence [26/26] |
|
| 433 | +*:cat0:C major scale played from an array of frequencies using a loop [52/52] |
|
| 434 | +*:cat0:sample_distance() completed: values sorted, true median returned [39/39] |
|
| 435 | +*:cat0:code tracked in lab46 semester repo [13/13] |
|
| 436 | +``` |
|
| 437 | + |
|
| 438 | +### Additionally |
|
| 439 | + |
|
| 440 | + * Solutions not abiding by spirit of project will be subject to a 50% overall deduction |
|
| 441 | + * Solutions not utilizing descriptive why and how comments will be subject to a 25% overall deduction |
|
| 442 | + * Solutions not utilizing indentation to promote scope and clarity or otherwise maintaining consistency in code style and presentation will be subject to a 25% overall deduction |
|
| 443 | + * Solutions not organized and easy to read (assume a terminal at least 90 characters wide, 40 characters tall) are subject to a 25% overall deduction |
haas/fall2026/c4eng/projects/cat1.md
| ... | ... | @@ -0,0 +1,356 @@ |
| 1 | +# ENGR1050 C for Engineers |
|
| 2 | + |
|
| 3 | +# PROJECT: Create A Theremin (CAT1) |
|
| 4 | + |
|
| 5 | +## OBJECTIVE |
|
| 6 | + |
|
| 7 | +Make music (or at least noise) by waving your hand around! We bring |
|
| 8 | +together the ultrasonic distance sensor and passive buzzer we explored in |
|
| 9 | +cat0, and have our program turn distance into pitch. |
|
| 10 | + |
|
| 11 | +## PROCESS |
|
| 12 | + |
|
| 13 | +Do note, the productive way to go about this project involves taking the |
|
| 14 | +following steps: |
|
| 15 | + |
|
| 16 | + * starting early |
|
| 17 | + * reading the project page |
|
| 18 | + * asking questions regarding things you do not know, are not clear on, |
|
| 19 | + or are confused about |
|
| 20 | + * as information, concepts, processes become clear, that is something |
|
| 21 | + you can contribute to the project documentation (so you can better |
|
| 22 | + remember) |
|
| 23 | + |
|
| 24 | +If you start too late, and do not ask questions, and do not have enough |
|
| 25 | +time and don't know what is going on, you are not doing the project |
|
| 26 | +correctly. |
|
| 27 | + |
|
| 28 | +## OBTAIN THE PROJECT FILES |
|
| 29 | + |
|
| 30 | +To assist with consistency across all implementations, data files for use |
|
| 31 | +with this project are available on lab46 as a `grabit`. Please copy the |
|
| 32 | +data to your private semester repository under an appropriate project |
|
| 33 | +directory to begin work, and ensure your implementation properly works |
|
| 34 | +with the provided data. |
|
| 35 | + |
|
| 36 | +The project should be obtained by the **Sunday prior to the due date**. |
|
| 37 | + |
|
| 38 | +``` |
|
| 39 | +lab46:~/SEMESTER/c4eng$ grabit c4eng cat1 |
|
| 40 | +``` |
|
| 41 | + |
|
| 42 | +Please study any provided code and supporting documents, and look up, |
|
| 43 | +experiment, and ask questions on aspects that you do not understand. |
|
| 44 | + |
|
| 45 | +## OPERATING THE PROJECT |
|
| 46 | + |
|
| 47 | +These projects will have two paths to them, depending on which device you |
|
| 48 | +are using: |
|
| 49 | + |
|
| 50 | + * Raspberry Pi single board computer (4/5) using the `wiringPi` library |
|
| 51 | + * Raspberry Pi Pico microcontroller |
|
| 52 | + |
|
| 53 | +``` |
|
| 54 | +lab46:~/SEMESTER/c4eng/cat1$ make |
|
| 55 | +Please select which development environment: |
|
| 56 | + |
|
| 57 | + make wiringpi - using the Raspberry Pi with wiringpi library |
|
| 58 | + make pico - using the Raspberry Pi pico |
|
| 59 | + |
|
| 60 | +after which, you can use 'make' to build |
|
| 61 | +lab46:~/SEMESTER/c4eng/cat1$ |
|
| 62 | +``` |
|
| 63 | + |
|
| 64 | +Run the `make` command with the category representing the path you will |
|
| 65 | +be working with for this project. |
|
| 66 | + |
|
| 67 | +After which, if you do an `ls`, you will see a cyan-colored file called |
|
| 68 | +`cat1.c`. This is the C file you will be editing for this project. |
|
| 69 | + |
|
| 70 | +Go ahead and view it, study the code, eventually make any changes, save |
|
| 71 | +and return to the command-line. |
|
| 72 | + |
|
| 73 | +To build it, you simply run: `make` |
|
| 74 | + |
|
| 75 | +If there are no errors, it will build, and you proceed from there. |
|
| 76 | + |
|
| 77 | +## MAINTAINING YOUR REPOSITORY |
|
| 78 | + |
|
| 79 | +Be sure to keep your repository up-to-date, regularly committing changes |
|
| 80 | +to files. |
|
| 81 | + |
|
| 82 | +As a reminder, here are the suggested steps: |
|
| 83 | + |
|
| 84 | + -1. `make clean` <-- remove all unnecessary files |
|
| 85 | + 0. `git status` <-- see the current state of things |
|
| 86 | + 1. `git pull` <-- pull down any changes from elsewhere |
|
| 87 | + 2. `git add` <-- add each new or modified file |
|
| 88 | + 2a. `git add ./` <-- try this the first time |
|
| 89 | + 3. `git commit -m "your message"` <-- make a snapshot |
|
| 90 | + 4. `git push` |
|
| 91 | + |
|
| 92 | +## UNDERSTANDING THE TECHNOLOGY |
|
| 93 | + |
|
| 94 | +A **theremin** is an unusual musical instrument: you play it without |
|
| 95 | +touching it. The performer moves their hands near (but not touching) a |
|
| 96 | +pair of antennas, and the distance of the hand from an antenna controls |
|
| 97 | +the pitch (one hand) and volume (the other). We're going to build a |
|
| 98 | +simplified cousin of this with the parts in our kit: one "antenna" (the |
|
| 99 | +ultrasonic sensor), controlling pitch. |
|
| 100 | + |
|
| 101 | +You already know how the pieces work from cat0: |
|
| 102 | + |
|
| 103 | + * the sensor gives us a time, which we convert to a distance |
|
| 104 | + * the passive buzzer makes whatever frequency we tell it to |
|
| 105 | + |
|
| 106 | +What is new is the connection between them. We need a **mapping**: a rule |
|
| 107 | +that takes a distance and produces a frequency. Mappings are everywhere |
|
| 108 | +in engineering (a thermostat maps a temperature to a heater setting; a |
|
| 109 | +throttle pedal maps a position to an engine speed). Designing one |
|
| 110 | +involves choices, and no single choice is "right": |
|
| 111 | + |
|
| 112 | + * which direction? Does bringing your hand closer raise the pitch, or |
|
| 113 | + lower it? |
|
| 114 | + * over what range? Sensor readings span 2 to 200cm, but your arm |
|
| 115 | + doesn't. Pick the nearest and farthest distances that matter |
|
| 116 | + * to what frequencies? The buzzer will be easiest to hear near its |
|
| 117 | + 2000Hz resonance, but a theremin is no fun with only a narrow range |
|
| 118 | + * with what shape? Does moving your hand 10cm change the pitch by the |
|
| 119 | + same amount no matter where you are in the range? |
|
| 120 | + |
|
| 121 | +On that last point: our ears do not perceive pitch linearly. Going from |
|
| 122 | +200Hz to 400Hz sounds like the same musical interval (an octave) as going |
|
| 123 | +from 1000Hz to 2000Hz, even though the first difference is 200Hz and the |
|
| 124 | +second is 1000Hz. A mapping in which equal movements of your hand produce |
|
| 125 | +equal *ratios* of frequency will therefore sound more natural than one |
|
| 126 | +producing equal *differences*. Try both! |
|
| 127 | + |
|
| 128 | +## TASK |
|
| 129 | + |
|
| 130 | +Taking our knowledge of GPIOs, the ultrasonic sensor, and the passive |
|
| 131 | +buzzer: |
|
| 132 | + |
|
| 133 | + * assemble the circuit from cat0 (if it is still standing, you are |
|
| 134 | + done with this step) |
|
| 135 | + * rework the provided code so that the pitch of the buzzer follows the |
|
| 136 | + distance of your hand from the sensor, as described below |
|
| 137 | + * play it! Post a video of you performing to the class DISCORD |
|
| 138 | + |
|
| 139 | + |
|
| 140 | + |
|
| 141 | +### The circuit |
|
| 142 | + |
|
| 143 | +It's the same as in cat0. For quick reference (pin choices are defined at |
|
| 144 | +the top of your `cat1.c` file; if you wire to different pins, change the |
|
| 145 | +values there): |
|
| 146 | + |
|
| 147 | +| Connection | Raspberry Pi | Pico | |
|
| 148 | +| ---------------------------- | --------------------------- | -------------------------- | |
|
| 149 | +| Sensor VCC | 5V (physical pin 2) | VBUS (physical pin 40) | |
|
| 150 | +| Sensor GND | GND (physical pin 6) | GND (physical pin 38) | |
|
| 151 | +| Sensor Trig | wiringPi 4 (GPIO23, pin 16) | GP2 (physical pin 4) | |
|
| 152 | +| Sensor Echo (via divider) | wiringPi 5 (GPIO24, pin 18) | GP3 (physical pin 5) | |
|
| 153 | +| Buzzer (via 1k and S8050) | wiringPi 0 (GPIO17, pin 11) | GP4 (physical pin 6) | |
|
| 154 | + |
|
| 155 | +The sensor's Echo MUST go through the voltage divider (1k ohm to the |
|
| 156 | +GPIO, then two 1k ohm resistors in series to ground), as the Echo output |
|
| 157 | +is 5 volts and our GPIOs are not 5 volt tolerant. The buzzer's positive |
|
| 158 | +leg connects to 5V, its negative leg to the transistor's collector, the |
|
| 159 | +transistor's emitter to ground, and the GPIO connects to the transistor's |
|
| 160 | +base through a 1k ohm resistor. See cat0 for the details. |
|
| 161 | + |
|
| 162 | +### The theremin |
|
| 163 | + |
|
| 164 | +The code you are given has ONE threshold: if something is closer than |
|
| 165 | +`THRESHOLD`, a single steady tone sounds (and on the pico, the onboard |
|
| 166 | +LED lights), otherwise it is silent. It's a doorbell, not an instrument. |
|
| 167 | + |
|
| 168 | +Your task is to make the pitch a continuous function of distance: |
|
| 169 | + |
|
| 170 | + * if no echo was received, or the object is farther than the farthest |
|
| 171 | + distance you chose: silence |
|
| 172 | + * otherwise, calculate a frequency from the distance and sound it, |
|
| 173 | + such that the pitch follows your hand smoothly as it moves |
|
| 174 | + * at or closer than the nearest distance you chose, the pitch stays at |
|
| 175 | + its extreme (it does not keep climbing or falling, nor wrap around |
|
| 176 | + or go negative) |
|
| 177 | + * the frequency you produce must always remain within the lowest and |
|
| 178 | + highest frequencies you chose |
|
| 179 | + * on the pico, the onboard LED lights while the instrument is sounding. |
|
| 180 | + On the Raspberry Pi, display the distance and frequency being played |
|
| 181 | + * on the Raspberry Pi, the distance must still be reported (the pico, |
|
| 182 | + having no screen, is judged by its pitch and its LED) |
|
| 183 | + |
|
| 184 | +Some guidance: |
|
| 185 | + |
|
| 186 | + * a function that is given a distance and returns a frequency, |
|
| 187 | + `distance_to_frequency()`, is provided in your source file, along |
|
| 188 | + with starting values for the range (`NEARDIST`, `FARDIST`, `LOWFREQ`, |
|
| 189 | + `HIGHFREQ`). It works, but is not yet the instrument you want: as |
|
| 190 | + given, farther is *higher*, and the sweep is linear. Read its |
|
| 191 | + comments, make the changes they ask for, and **hook it up in |
|
| 192 | + `main()`** (it is not called as given). Test it by itself (does the |
|
| 193 | + nearest distance give your lowest or highest frequency, as intended? |
|
| 194 | + What about halfway between?) |
|
| 195 | + |
|
| 196 | + * a convenient way to think about the mapping is to first find how far |
|
| 197 | + along your range of distances the hand is, as a **fraction** from 0 |
|
| 198 | + (at the nearest distance) to 1 (at the farthest): |
|
| 199 | + |
|
| 200 | + ``` |
|
| 201 | + fraction = (distance - nearest) / (farthest - nearest) |
|
| 202 | + ``` |
|
| 203 | + |
|
| 204 | + then use that fraction to find your way between your frequencies. |
|
| 205 | + Be mindful of integer versus floating point division! |
|
| 206 | + * to move linearly between two values, you scale the difference by the |
|
| 207 | + fraction, and add it to where you started. To move by equal |
|
| 208 | + *ratios*, you instead scale the *ratio* of the endpoints... which |
|
| 209 | + involves raising it to the power of the fraction (see `pow()` in |
|
| 210 | + `math.h`) |
|
| 211 | + * carry over what you did in cat0: copy your finished `sample_distance()` |
|
| 212 | + (and the constants it needs) into `cat1.c`, and use it to keep the |
|
| 213 | + pitch from jittering. But consider the trade-off you |
|
| 214 | + were asked about there: if your instrument takes a third of a second |
|
| 215 | + to notice your hand has moved, it will feel sluggish. How many |
|
| 216 | + samples (and how long a pause between them) make for a pleasant |
|
| 217 | + instrument? Choose your values deliberately, and explain your choice |
|
| 218 | + in a comment |
|
| 219 | + * you will find that your instrument is much more pleasant to play if |
|
| 220 | + you pick your constants (nearest and farthest distance, lowest and |
|
| 221 | + highest frequency, number of samples, delays) with some thought, and |
|
| 222 | + give each a descriptive `#define` name. Plan to try several settings |
|
| 223 | + * on the Raspberry Pi, the buzzer's tones are generated by a background |
|
| 224 | + thread that shares the processor with the rest of your program. If |
|
| 225 | + you notice odd behavior in your readings while the buzzer is sounding, |
|
| 226 | + that may be why. Make note of it in your comments if you do |
|
| 227 | + * the sensor is most reliable when aimed squarely at a flat surface. |
|
| 228 | + Your hand will do, but your sleeve may not! |
|
| 229 | + |
|
| 230 | +For those wishing to go further (not required, and no extra credit, but |
|
| 231 | +cool): instead of letting the pitch slide freely, snap it to the nearest |
|
| 232 | +note of a musical scale (use the array of frequencies from cat0), and |
|
| 233 | +your hand will be able to play recognizable tunes. How wide a band of |
|
| 234 | +distance should each note get? |
|
| 235 | + |
|
| 236 | +## EXECUTION |
|
| 237 | + |
|
| 238 | +If on the pico, follow the process for downloading the binary file and |
|
| 239 | +programming your pico. The pico has no screen: the onboard LED lights |
|
| 240 | +while your instrument is sounding, and what you hear is your feedback. |
|
| 241 | + |
|
| 242 | +If on the full Raspberry Pi, you have a green executable you can run just |
|
| 243 | +as we've done in our other projects. |
|
| 244 | + |
|
| 245 | +## STRATEGY |
|
| 246 | + |
|
| 247 | +The general flow of the process (one way of going about it, anyway) can |
|
| 248 | +be described as follows: |
|
| 249 | + |
|
| 250 | +``` |
|
| 251 | +INITIALIZE THE GPIOS AND THE BUZZER |
|
| 252 | +REPEAT INFINITELY: |
|
| 253 | + OBTAIN THE MEDIAN DISTANCE |
|
| 254 | + |
|
| 255 | + SHOULD THERE BE NO READING, OR THE OBJECT BE BEYOND THE FARTHEST: |
|
| 256 | + SILENCE THE BUZZER |
|
| 257 | + TURN OFF THE LED (IF ON THE PICO) |
|
| 258 | + REPORT THAT NOTHING IS THERE (ON THE PI) |
|
| 259 | + OTHERWISE: |
|
| 260 | + DETERMINE THE FREQUENCY FOR THE DISTANCE |
|
| 261 | + SOUND THE BUZZER AT THAT FREQUENCY |
|
| 262 | + TURN ON THE LED (IF ON THE PICO) |
|
| 263 | + REPORT THE DISTANCE AND FREQUENCY (ON THE PI) |
|
| 264 | + |
|
| 265 | + DELAY BRIEFLY |
|
| 266 | + |
|
| 267 | +REPEAT BLOCK |
|
| 268 | +``` |
|
| 269 | + |
|
| 270 | +And for your frequency function, a linear approach: |
|
| 271 | + |
|
| 272 | +``` |
|
| 273 | +GIVEN A DISTANCE: |
|
| 274 | + SHOULD THE DISTANCE BE AT OR BELOW THE NEAREST: |
|
| 275 | + TREAT IT AS THE NEAREST |
|
| 276 | + SHOULD THE DISTANCE BE ABOVE THE FARTHEST: |
|
| 277 | + TREAT IT AS THE FARTHEST |
|
| 278 | + FIND HOW FAR ALONG THE RANGE THE DISTANCE IS, AS A FRACTION OF 0 TO 1 |
|
| 279 | + MOVE THAT FRACTION OF THE WAY FROM ONE END OF THE FREQUENCY RANGE TO |
|
| 280 | + THE OTHER |
|
| 281 | + RETURN THE RESULTING FREQUENCY |
|
| 282 | +``` |
|
| 283 | + |
|
| 284 | +## SUBMISSION |
|
| 285 | + |
|
| 286 | +To be successful in this project, the following criteria (or their |
|
| 287 | +equivalent) must be met: |
|
| 288 | + |
|
| 289 | + * Project must be submit on time, by the deadline. |
|
| 290 | + * Late submissions will lose 33% credit per day, with the submission |
|
| 291 | + window closing on the 3rd day following the deadline. |
|
| 292 | + * All code must compile cleanly (no warnings or errors) |
|
| 293 | + * Compile with the `-Wall` and `--std=gnu18` compiler flags |
|
| 294 | + * All requested functionality must conform to stated requirements |
|
| 295 | + (either in this document or in a comment banner in the source code |
|
| 296 | + files themselves). |
|
| 297 | + * Executed programs must display in a manner similar to provided output |
|
| 298 | + * Output formatted, where applicable, must match that of project |
|
| 299 | + requirements |
|
| 300 | + * Processing must be correct based on input given and output requested |
|
| 301 | + * Output, if applicable, must be correct based on values input |
|
| 302 | + * Code must be nicely and consistently indented |
|
| 303 | + * Code must be consistently written, to strive for readability from |
|
| 304 | + having a consistent style throughout |
|
| 305 | + * Code must be commented |
|
| 306 | + * Any "to be implemented" comments **MUST** be removed |
|
| 307 | + * These "to be implemented" comments, if still present at |
|
| 308 | + evaluation time, will result in points being deducted. |
|
| 309 | + * Sufficient comments explaining the point of provided logic **MUST** |
|
| 310 | + be present |
|
| 311 | + * No global variables (without instructor approval), no goto |
|
| 312 | + statements, no calling of main()! |
|
| 313 | + * Track/version the source code in your lab46 semester repository |
|
| 314 | + * Submit a copy of your source code to me using the `submit` tool |
|
| 315 | + (`make submit` on lab46 will do this) by the deadline. |
|
| 316 | + |
|
| 317 | +### Submit Tool Usage |
|
| 318 | + |
|
| 319 | +Let's say you have completed work on the project, and are ready to |
|
| 320 | +submit, you would do the following: |
|
| 321 | + |
|
| 322 | +``` |
|
| 323 | +lab46:~/SEMESTER/c4eng/cat1$ make submit |
|
| 324 | +``` |
|
| 325 | + |
|
| 326 | +You should get some sort of confirmation indicating successful submission |
|
| 327 | +if all went according to plan. If not, check for typos and or locational |
|
| 328 | +mismatches. |
|
| 329 | + |
|
| 330 | +What I'll be looking for: |
|
| 331 | + |
|
| 332 | +### RUBRIC |
|
| 333 | + |
|
| 334 | +I'll be evaluating the project based on the following criteria: |
|
| 335 | + |
|
| 336 | +``` |
|
| 337 | +312:cat1:final tally of results (312/312) |
|
| 338 | +*:cat1:used grabit to obtain project by the Sunday prior to duedate [13/13] |
|
| 339 | +*:cat1:post a short video of your theremin to the class DISCORD [13/13] |
|
| 340 | +*:cat1:sensor and buzzer hooked to unique GPIOs [26/26] |
|
| 341 | +*:cat1:distance correctly measured and reported [26/26] |
|
| 342 | +*:cat1:no echo, out of range silences the buzzer (no hang) [26/26] |
|
| 343 | +*:cat1:distance-to-frequency function completed and tuned [52/52] |
|
| 344 | +*:cat1:pitch changes continuously as the distance changes [78/78] |
|
| 345 | +*:cat1:frequency kept within chosen bounds [26/26] |
|
| 346 | +*:cat1:readings smoothed with median of samples (from cat0) [26/26] |
|
| 347 | +*:cat1:LED (pico), frequency display (Pi) indicates instrument sounding [13/13] |
|
| 348 | +*:cat1:code tracked in lab46 semester repo [13/13] |
|
| 349 | +``` |
|
| 350 | + |
|
| 351 | +### Additionally |
|
| 352 | + |
|
| 353 | + * Solutions not abiding by spirit of project will be subject to a 50% overall deduction |
|
| 354 | + * Solutions not utilizing descriptive why and how comments will be subject to a 25% overall deduction |
|
| 355 | + * Solutions not utilizing indentation to promote scope and clarity or otherwise maintaining consistency in code style and presentation will be subject to a 25% overall deduction |
|
| 356 | + * Solutions not organized and easy to read (assume a terminal at least 90 characters wide, 40 characters tall) are subject to a 25% overall deduction |