Author Archives: Robert Walsh

Configuring a Classroom for Distance Learning (on a budget!)

A small private school that serves male students from at-risk neighborhoods asked me to teach a course remotely to their 10th grade students.  One of the first challenges we faced was configuring the classroom for distance learning to provide an appropriate environment for conducting the daily lessons.  The school has Internet access and a campus-wide wireless network. Several of the classrooms have SMART MX086 interactive display boards from SMART Technologies.  Additionally, each student has a Chromebook with camera, microphone, and speakers.

A Problem with Feedback

During an initial site visit, we started a test session with Zoom.  I hosted the meeting and took my laptop into one classroom. The Head of School remained with his connected via HDMI to a SMART board in another.  We immediately discovered there was going to be a problem with audio feedback. The mics in the Chromebooks amplify the sound from the board’s built-in speakers and produce a screeching hiss.  I concluded my visit with an action item to design a solution that would reduce or eliminate this feedback.

After doing some research, I discovered that cardioid microphones are designed for this scenario.  A cardioid microphone picks up sound to the side and in front of the microphone, but not from behind it.  They are so named because the mic’s sensitive area roughly resembles a heart when drawn in cross-section.  I purchased two of these cardioid microphones with long cables and stands. I also bought a small audio mixer and a cable to connect its output to the SMART board’s audio in port.  We planned to position the microphones on either side of the room, facing away from the SMART board. Running them through the mixer would provide a single, merged audio signal to the SMART board.

Testing the Planned Solution

I returned to the school a day before the course was to start. We wanted to set up and test the audio configuration.  Sadly, it did not work.  With a laptop connected to one of the SMART board’s HDMI input ports, we could not get any sound from the mixer.  Further, we could not route the signal from the mixer directly to the laptop because the laptop had only a combined microphone/headphones jack.  This jack needs a cable with a special tip that carries the left and right output signals to the headphones as well as the audio input signal from the microphone.  Even if we had had an appropriate cable, this port expects an input signal with much less voltage than the mixer was supplying.  (The mixer needs to go into a line in port rather than a microphone port.)

We discovered that the SMART board runs an Android operating system and had a Zoom client available for it.  We believed, therefore, that we could eliminate the laptop. With the native Zoom client, we thought we could use the mixer (connected to the board’s audio in port) as the microphone.  This also did not work.  We ultimately learned that the SMART board’s audio in port is active ONLY when the board is connected to a laptop’s display using the VGA port.  Unlike HDMI, VGA does not carry an audio signal. To get sound from the laptop when driving the board’s display via VGA, one must connect the laptop’s audio out/headphones port to the board’s audio in port.  When connected this way, it seems that the audio signal from the laptop is routed directly to the board’s speakers.

Eureka!

I realized that what we probably needed was a USB external sound card with a line in port.  The would allow us to route the mixer’s output into the laptop. I found two that looked promising: the Startech 7.1 USB Sound Card and the Envel Headset Adapter (which isn’t really a sound card; instead, it is an adapter that detects what is plugged into it and adjusts accordingly).  Because they were relatively inexpensive (less than $40 combined), I ordered both.

When these new components arrived later that week, we were pleased to see that both worked!  Now back to using the laptop connected to the SMART board via HMDI, we can use either sound device. We plug the output cable from the mixer into the device and select the appropriate audio input sound source in the Zoom client. Each student also joins the meeting on his own Chromebook, but with the volume down and the microphone muted.  We positioned the two microphone stands near the front of the classroom, one on each side. They are almost even with the first row of desks, facing away from the SMART board.

Analyzing the Results

After configuring this classroom for distance learning, the sound quality is pretty good.  Students hear me through the SMART board’s speakers. There is virtually no feedback as long as they leave their microphones off. I can hear the class through the two microphones whose signals are being fed through the mixer into the laptop. The set up works well when calling on an individual students. It is difficult to hear who is speaking in more open class discussions, though.

To help compensate, class discussions are now a blend of lecture and challenge/response. I go around the seating chart and ask specific students to contribute.  In truth, this may actually help them stay engaged. After all, they do not know when I might ask them to answer a question. If I need to meet individually with a student, I first mute the main session. Then, I assign the student to a Zoom breakout room.  The student unmutes his microphone, turns up the volume on his Chromebook, and we can converse one-on-one.

Impact on the Bottom Line

Not including the equipment the school already owned, configuring this classroom for distance learning cost under $170. It would have worked without the Startech sound card thus bringing the cost to around $140.  Specifically, we purchased the following:

ITEMAPPROXIMATE COST (US dollars)
Ohuhu triple boom microphone stand (2-pack)$40
Amazon Basics dynamic vocal cardioid microphone w/cable2@$21
Donner DEL-8i2 4-channel stereo audio mixer$34
MillSO 6.35mm (1/4″) male to 3.5mm (1/8″) male audio cable$13
Startech 7.1 USB sound card$31
Envel USB headset adapter$9
Table 1: Approximate costs for equipment purchased to configure classroom for distance learning

Note: Neither I nor the Excalibur Solutions STEM Academy™ nor Excalibur Solutions is affiliated in any way with the companies that manufacture the products mentioned in this article, nor have any of the aforementioned parties been compensated for discussing their use. These products were purchased and are being used in the manner described in the article. No warranty, either expressed or implied, is made as to the suitability of these products for use in any context.

It’s Not All Fun and Games!

By Robert Walsh

What can creating a video game teach students about math? Quite a lot, actually. Today’s video games have become so realistic that, at a glance, one may think they are live action television. Development environments like Unity and GameMaker Studio handle much of the heavy lifting when it comes to the mathematical calculations necessary to move the in-game objects, to do the intense graphical rendering (also based on math), and to apply physical laws like gravity and inertia.

However, with a simpler environment like Scratch, students must do this work for themselves.  Even relatively simple games like Bricks Buster reinforce concepts identified in the Common Core State Standards Initiative like inequalities, fractions and decimals, geometric transformations, and the x-y coordinate plane.

Bricks Buster stage with coordinate plane overlay.

Bricks Buster also uses recursion to draw the rows of bricks, thus allowing it to connect with a lesson on the Fibonacci sequence, for example.

Other projects in our Learn Programming by Creating Video Games unit require students to use even more complex concepts like working with vectors and applying trigonometric relationships in order to update a sprite’s position in each iteration of the game loop.

So, while it may look like students are simply playing games, they are actually learning and applying important mathematical concepts. In addition, they get to see these concepts without the artificial separation often imposed by traditional curricula.

If this looks like a fun way to learn math, we can help! We have programs for schools and for individuals spanning a wide range of subject areas including game development. Send us a note to let us know you are interested!

Scratch as a Programming Environment

By Robert Walsh

I have been programming for a long time. I started writing commercial applications in the middle 1990s, but my first exposure to computers was in middle school. Whether it be on the TRS-80 Color Computer or a TI99-4/a, I could

10 PRINT "HELLO"
20 GOTO 10

RUN

with the best on them. In high school, I learned a bit of Pascal using the Apple IIe, graduating to Turbo Pascal 5 on an IBM-compatible 80286 in college. From there, a whole host of other languages, including C/C++, C#, Java, JavaScript, and so on. One thing they all had in common was that they were text-based. Even though Visual Basic and rest of the languages available in Microsoft’s Visual Studio supported a visual editor for the GUI, the code to do things in response to user interaction was still written in text.

Programming with Puzzle Pieces

So, when I first learned about Scratch, I was not particularly impressed. “What can a programmer do just by dragging puzzle pieces around,” I thought. I tried to interest my daughter in Scratch, but we didn’t get much farther than moving the cat around the screen and switching its costumes.

Now that I have looked more closely at Scratch and how it can be used to introduce programming to those that have never done it before, I have gained newfound respect for Scratch as a programming environment. Yes, it is colorful and cartoony, but that does not mean it should be limited only to kids under a certain age.

Scratch as a Teaching Tool

To me, Scratch does two things that make it a great tool for teaching programming.

First, Scratch frees the programmer from having to remember what are often obscure syntax rules and language constructs. For example, even though I can write essentially the same program in Java, C#, or Python, the punctuation I use, the standard conventions for naming things, and even the physical structure of the code will be different. As a result, the programmer can quickly become overwhelmed trying to learn both concepts and semantics. By giving programmers an organized toolbox of available commands and operations, Scratch lets them focus on what they want to accomplish rather than the language-specific details for how to do it.

Second, Scratch allows the programmer to see the structure of the program being created in ways that are not necessarily obvious in text-based environments. Conditionals and loops are two examples. In C-like languages, the curly braces do denote blocks of code, as do indents in Python; however, these can become challenging to match when there is more than one level of nesting (and infinitely more challenging without proper and consistent indenting, something Python at least enforces).

With Scratch, the color coded shapes clearly show the pieces of code that belong inside because they are literally surrounded. Some might consider the fact that the if/else construct in Scratch does not support multiple else clauses to be a limitation. In my view, though, this emphasizes the fact that an if/else construct is really just an either/or. The “if” condition is either true or it isn’t. In the case that it isn’t, any other tasks that need to be done are part of the “else” clause of the initial check. The if/else if/else construct is really just shorthand for what should be considered a nested set of binary choices.

Give Scratch a Try!

If you are wanting to learn to program but you aren’t sure where to start, give Scratch a try. There are many self-guided tutorials that will walk you through creating all kinds of programs. Or, contact us! Even if you are an experienced programmer, take a look at Scratch and see if it changes anything about the way you think about your programs.

Extension: Exploring Color with an Arduino and an RGB LED

By Robert Walsh

In a previous lesson, we built a circuit to control an RGB LED using three buttons and an Arduino. The buttons allowed us to cycle each individual color through five states: OFF 0%), DIM (25%), HALF (50%), SEMI (75%), and FULL (100%). This approach limited the number of distinct colors that could be represented by the RGB LED. With three colors and five possible values for each color, a total of 125 colors could be represented (5x5x5). The RGB LED, however, is capable of using a number between 0 and 255 for each of its colors, and that means that is can display over 16 million colors (256x256x256)!

In this extension, we will replace the three buttons with a single button and a potentiometer. A potentiometer is a type of variable resistor, and it can provide the Arduino with a value between 0 and 1023. We will convert the value from the potentiometer into a number between 0 and 255, and we will use the button to indicate which of the color’s values should be set. While this is not an ideal solution from a user experience stand point, it will allow us to produce a much wider range of colors than we could with the circuit with three buttons. (A better solution would require three potentiometers, one for each of the colors. However, the ELEGOO Mega 2560 Starter Kit includes only two potentiometers, so we are limiting ourselves to the components we have available.)

To complete this activity, you will need:

  • An Arduino Mega 2560 (other models will also work, but the pin numbers will be different)
  • One red, one blue, and one green LED
  • One common cathode RGB LED
  • Six 220 Ohm or 330 Ohm resistors
  • One push buttons
  • One 5K Ohm or 10K Ohm resistors
  • One 10k potentiometer
  • A breadboard
  • Several male-to-male jumper wires
  • A computer and USB cable to program the Arduino

Note: All of the components needed for this lesson (except the computer) are available in the ELEGOO Mega 2560 Starter Kit available from Amazon.

Controlling the RED LED

We will start by building a circuit that uses the potentiometer to set just the red color value. Here is the wiring diagram:

Wiring diagram for a circuit to control the value of the RED LED using a potentiometer

The potentiometer has three legs. The left-most leg connects to the common power rail on the breadboard. The right-most leg connects to the common ground (GND) rail. The middle leg connects to one of the analog pins on the Arduino (I used pin A0). When the dial on the potentiometer is turned all the way to the left, the resistance is almost 0 and the signal pin reads the maximum value (1023). When the dial is turned all the way to the right, the resistance is near 10k Ohms, and the signal pin reads the minimum value (0). The dial varies the resistance and allows more or less current to flow through the signal pin.

Note: The potentiometer is a large component, and it may need to span the center trough on the breadboard. If so, the center pin may be on the opposite side of the trough from the left and right pins. Be sure to connect the jumper wires on the correct sides of the breadboard. For example, using the “front” half of the breadboard for the jumper wire that connects to the analog pin on the Arduino when the center pin is on the “back” half will not work.

For now, we are connecting only the red leg and the common cathode (negative leg) of the RGB LED. The common cathode connects to GND, and the red leg connects to a 220 Ohm or 330 Ohm resistor. The other leg of the resistor connects to a pulse-width modulated (PWM) output pin on the Arduino (I used pin 7). Finally, the cathode of the red LED connects to GND, and the anode (positive leg) connects to a 220 Ohm or 330 Ohm resistor whose other leg connects to a PWM-capable output pin on the Arduino (I used pin 4).

Here is the Arduino sketch for this circuit:

const int POT = A0;
const int RGB_RED = 7;
const int RED = 4;

void setup() 
{
  pinMode(POT, INPUT);
  pinMode(RGB_RED, OUTPUT);
  pinMode(RED, OUTPUT);
}

void loop() 
{
  int potValue = analogRead(POT);
  int redValue = map(potValue, 0, 1023, 0, 255);
  analogWrite(RED, redValue);
  analogWrite(RGB_RED, redValue);
}

In the setup function, we set each of the pins as either inputs or outputs. In the loop function, we first read the value from the potentiometer (which will be a number between 0 and 1023). We then convert it to a number between 0 and 255 using the built-in map function. The map function takes a value as its first parameter, the low and high values for the “from” range, and the low and high values from the “to” range. The map function linearly scales the value from the “from” range into the “to” range. Finally, we use analogWrite to write the resulting value to the red LED and the red leg of the RGB led.

Once the sketch is compiled and uploaded to the Arduino, turning the dial on the potentiometer to the right (increasing the resistance) should make the LEDs dimmer, and turning to the left (reducing the resistance) should make them brighter.

Adding the Button and Green LED

With just one LED, we do not need any way to determine color value is being set. Since there is only one, the we can assume the potentiometer is controlling it. But when we add a second LED, we also need a button to indicate which LED’s value should be set by the potentiometer.

Here is the wiring diagram that adds the button and green LED:

Wiring diagram for a circuit to control the values of the red and green LEDs using a potentiometer and a button

As with the original lesson, we are using a 5k Ohm or 10k Ohm pull down resistor for the button. Otherwise, the changes should be straightforward. The button’s negative leg is connected to both the pull down resistor (which is connected to GND) and to an input pin on the Arduino (I used pin 8). The two new resistors are 220 Ohm or 330 Ohm, and each needs to connect to an output pin on the Arduino (I used pin 6 for the green leg of the RGB LED and pin 3 for the green LED).

Here is the modified Arduino sketch:

const int POT = A0;
const int BTN = 8;
const int RGB_RED = 7;
const int RGB_GREEN = 6;
const int RED = 4;
const int GREEN = 3;

int redValue;
int greenValue;

int lastPotValue;
bool wasPressed;
int selectedColor;  // 0 = unselected, 1 = red, 2 = green

int getLedIntensity(int potValue)
{
  /* 
   * Convert the value read from the potentiometer (between 0 and 1023) 
   * to a value that can be written to the LED (between 0 and 255) 
   */
  return map(potValue, 0, 1023, 0, 255);  
}

void setLed(int which, bool on, int value)
{
  /*
   * If the LED is supposed to be on, write its value
   * otherwise, turn it off
   */
  if (on)
  {
    analogWrite(which, value);  
  }
  else
  {
    digitalWrite(which, LOW);
  }
}

void setLeds(int currentColor, int red, int green)
{
  /*
   * Set the value of the individual LED if it is the current color, 
   * then set the value of each component color of the RGB LED
   */
  setLed(RED, currentColor == 1, red);
  setLed(GREEN, currentColor == 2, green);

  analogWrite(RGB_RED, red);
  analogWrite(RGB_GREEN, green);
}

void setup() 
{
  pinMode(POT, INPUT);
  pinMode(BTN, INPUT);
  pinMode(RGB_RED, OUTPUT);
  pinMode(RGB_GREEN, OUTPUT);
  pinMode(RED, OUTPUT);
  pinMode(GREEN, OUTPUT);

  // set initial values
  wasPressed = false;
  selectedColor = 0;
  lastPotValue = analogRead(POT);
  int ledIntensity = getLedIntensity(lastPotValue);
  redValue = ledIntensity;
  greenValue = ledIntensity;

  setLeds(selectedColor, redValue, greenValue);
}

bool debounce(bool lastState)
{
  /*
   * Determine if the button is pressed (ignoring spurious values during press or release)
   */
  bool isPressed = digitalRead(BTN);
  if (isPressed != lastState)
  {
    delay(5);
    isPressed = digitalRead(BTN);
  }
  return isPressed;  
}

void loop() 
{
  bool isPressed = debounce(wasPressed);
  // if the button is pressed now but was not pressed before
  if (isPressed && !wasPressed)
  {
    // switch to the next color (and "roll over" if at the end of the valid colors)
    selectedColor = (selectedColor + 1) % 3;

    // read the state of the potentiometer
    lastPotValue = analogRead(POT);  
  }
  wasPressed = isPressed;

  // if a color is selected
  if (selectedColor != 0)
  {
    // read the state of the potentiometer again
    int potValue = analogRead(POT);

    // if the potentiometer has changed since this color was selected
    if (potValue != lastPotValue)
    {
      // then set the appropriate color value
      switch (selectedColor)
      {
        case 1: // red
          redValue = getLedIntensity(potValue);
          break;

         case 2: // green
          greenValue = getLedIntensity(potValue);
          break;
      }

      // "remember" the potentiometer's value for the next time through loop
      lastPotValue = potValue;
    }
  }

  // set the LEDs based on the current values
  setLeds(selectedColor, redValue, greenValue);
}

This code is a bit more complicated that the original. Because we now need to keep track of the value of two different colors, we need to use global variables to “remember” them each time the loop function is executed. We have also added some functions to help us organize and separate some of the logic. This makes the main part of the code easier to read, and it helps to eliminate duplication when we need to do essentially the same thing more than once (e.g., for each of the LEDs).

The getLedIntensity function simply maps the potentiometer value between 0 and 1023 to a value between 0 and 255 than can be written to the LED.

The setLed function is used to set the value for the individual LEDs. We only want the individual LED to be lit when it is the selected color, so we pass two parameters in addition to the pin number for the LED: a boolean (true or false) value that indicates whether the LED should be on or off and an integer that contains the value for the LED if it is on. Where the setLed function is called, we have used a curious construct (see lines 46-47): currentColor == 1 for the call that sets the red LED. This is a short-hand way to say “if the currentColor is equal to 1.” In many computer languages, a double equals sign is used to mean “is equal to,” and it evaluates to either true or false. By using currentColor == 1 as a value to be passed as a parameter to the setLed function, we are telling the program to convert the expression into true (meaning currentColor is equal to 1) or false (meaning currentColor is not equal to 1) and pass the result to the function.

The setLeds function sets the values for all of the LEDs. In addition to calling setLed for each of the individual LEDs, it uses analogWrite to write the current value for each color to the appropriate leg of the RGB LED. Again, the individual LEDs will be on only when the corresponding color is the selected color, but the RGB LED will always set the value for each of the component colors. (This is why this circuit does not wire the individual legs of the RGB LED in series with the individual LEDs as we did in the original lesson.)

The debounce function was used in the original lesson. It reads the state of the button (i.e., whether it is pressed or not pressed), and it ignores spurious signals sent while the button is being pressed or released.

The loop function can be divided into three sections. The first (lines 88-99) determines whether the button is currently pressed but was not pressed the last time through the loop. If it is, we want to cycle to the next color. The selectedColor variable starts at 0 (which indicates no color is selected). When the button is pressed, we increment selectedColor so that its value becomes 1 (which indicates red is selected). When it is pressed again, we increment the value again to 2 (which indicates green is selected). When it is pressed a third time, the value becomes 3. However, we do not yet have another color, so we want selectedColor’s value to go back to 0. The % symbol represents the modulus operator, and modulus returns the remainder for integer division. For example, when selectedColor is initially 0, it is incremented to 1. 1 divided by 3 is 0 with a remainder of 1, so 1 % 3 = 1 (one modulus three equals one), so the new value for selectedColor is 1. When selectedColor is incremented to 2, 2 % 3 = 2 (two modulus three equals two), so the new value for selectedColor is 2. However, when selectedColor is incremented a third time, its value becomes 3. 3 % 3 = 0 (three modulus three equals zero because three divided by three equals one with no remainder; the modulus operator tells us the remainder from the division operation). Therefore, the new value for selectedColor is 0. This is a common way to increment a value across a range of sequential values without going beyond the last valid value.

In addition to incrementing the selectedColor when the button is pressed, this first part of the loop function also reads the current value from the potentiometer. This is because, when we move to the next color, we only want to reset its value if the potentiometer is moved after the color is selected. This allows us to bypass a color without changing its value.

The second part of the loop function (lines 102-124) checks to see if a color is currently selected (selectedColor is not 0). If so, we read the potentiometer again. If the value is the same as the last time we checked it, we do not want to make any changes to the color values. That way, just cycling through the colors without turning the potentiometer will not set all colors to the same value. Without this check, the green LED would immediately inherit the same value as the red LED as soon as the button is pressed. Now, though, the potentiometer has to be turned after green is selected before the value of the green LED is changed. However, if there is a new value, we set the value for the currently selected color. The switch statement is like a series of if/else statements. If there is a case block that matches selectedColor, those statements will be executed. The break statement is used to separate one case from another; without break, execution will “fall through” and continue into the next case. The switch statement also accepts a default block (which we did not use) that executes if none of the cases match (or if there are no break statements between the matching case and the default block).

The final part of the loop function (line 128) sets the values of all of the LEDs.

When this sketch is compiled and uploaded to the Arduino, the red and green LEDs should initially be off. However, the RGB LED might be lit with equal amounts of red and green based on the current position of the potentiometer. (If the potentiometer is turned all the way to the right, the RGB LED should be off.) Pressing the button once should select red, and turning the potentiometer should change the value of both the red LED and the RGB LED. Pressing the button again should select green. The red LED should go off, and the green LED should come on (unless the last value for green was 0). However, the amount of green in the RGB LED should not change until the potentiometer is turned. Turning the potentiometer should change both the green LED and the RGB LED. Pressing the button a third time returns the system to the “no color selected” state, and turning the potentiometer should have no effect.

Adding the Blue LED

The final change is to add the blue LED. Here is the new wiring diagram:

Wiring diagram for a circuit to control the red, green, and blue LEDs with a potentiometer and a button

Adding the blue LED is exactly the same is adding the green LED except for which pins o the Arduino are connected (I used pin 5 for the blue leg of the RGB LED and pin 2 for the blue LED).

The changes to the Arduino sketch are also relatively easy:

const int POT = A0;
const int BTN = 8;
const int RGB_RED = 7;
const int RGB_GREEN = 6;
const int RGB_BLUE = 5;
const int RED = 4;
const int GREEN = 3;
const int BLUE = 2;

int redValue;
int greenValue;
int blueValue;

int lastPotValue;
bool wasPressed;
int selectedColor;  // 0 = unselected, 1 = red, 2 = green, 3 = blue

int getLedIntensity(int potValue)
{
  /* 
   * Convert the value read from the potentiometer (between 0 and 1023) 
   * to a value that can be written to the LED (between 0 and 255) 
   */
  return map(potValue, 0, 1023, 0, 255);  
}

void setLed(int which, bool on, int value)
{
  /*
   * If the LED is supposed to be on, write its value
   * otherwise, turn it off
   */
  if (on)
  {
    analogWrite(which, value);  
  }
  else
  {
    digitalWrite(which, LOW);
  }
}

void setLeds(int currentColor, int red, int green, int blue)
{
  /*
   * Set the value of the individual LED if it is the current color, 
   * then set the value of each component color of the RGB LED
   */
  setLed(RED, currentColor == 1, red);
  setLed(GREEN, currentColor == 2, green);
  setLed(BLUE, currentColor == 3, blue);

  analogWrite(RGB_RED, red);
  analogWrite(RGB_GREEN, green);
  analogWrite(RGB_BLUE, blue);
}

void setup() 
{
  pinMode(POT, INPUT);
  pinMode(BTN, INPUT);
  pinMode(RGB_RED, OUTPUT);
  pinMode(RGB_GREEN, OUTPUT);
  pinMode(RGB_BLUE, OUTPUT);
  pinMode(RED, OUTPUT);
  pinMode(GREEN, OUTPUT);
  pinMode(BLUE, OUTPUT);

  // set initial values
  wasPressed = false;
  selectedColor = 0;
  lastPotValue = analogRead(POT);
  int ledIntensity = getLedIntensity(lastPotValue);
  redValue = ledIntensity;
  greenValue = ledIntensity;
  blueValue = ledIntensity;

  setLeds(selectedColor, redValue, greenValue, blueValue);
}

bool debounce(bool lastState)
{
  /*
   * Determine if the button is pressed (ignoring spurious values during press or release)
   */
  bool isPressed = digitalRead(BTN);
  if (isPressed != lastState)
  {
    delay(5);
    isPressed = digitalRead(BTN);
  }
  return isPressed;  
}

void loop() 
{
  bool isPressed = debounce(wasPressed);
  // if the button is pressed now but was not pressed before
  if (isPressed && !wasPressed)
  {
    // switch to the next color (and "roll over" if at the end of the valid colors)
    selectedColor = (selectedColor + 1) % 4;

    // read the state of the potentiometer
    lastPotValue = analogRead(POT);  
  }
  wasPressed = isPressed;

  // if a color is selected
  if (selectedColor != 0)
  {
    // read the state of the potentiometer again
    int potValue = analogRead(POT);

    // if the potentiometer has changed since this color was selected
    if (potValue != lastPotValue)
    {
      // then set the appropriate color value
      switch (selectedColor)
      {
        case 1: // red
          redValue = getLedIntensity(potValue);
          break;

        case 2: // green
          greenValue = getLedIntensity(potValue);
          break;

        case 3: // blue
          blueValue = getLedIntensity(potValue);
          break;
      }

      // "remember" the potentiometer's value for the next time through loop
      lastPotValue = potValue;
    }
  }

  // set the LEDs based on the current values
  setLeds(selectedColor, redValue, greenValue, blueValue);
}

It is important to add a new integer parameter, blueValue to the setLeds function (lines 43, 78, and 140). It is also important to change the calculation that determines the selectedColor (line 102). Since we now have another valid value, we need to take the remainder (modulus) of dividing by 4, not 3.

The behavior after compiling and uploading should be the same as before except that you can now set the value for blue.

Further Improvements

As was stated above, a better interface would involve three potentiometers, one dedicated to each of the colors. That would improve the code significantly since there would be no need to remember each colors value when it was not the selected color. Instead, each time through the loop, we would simply read each potentiometer and update the individual LEDs and the RGB LED. All of the LEDs could be on at the same time (as they were in the original lesson).

Also, using “raw” numeric values to represent states (e.g., 0 means unselected, 1 means red, etc.) makes it difficult for others to understand the code. The specific values are sometimes referred to as “magic numbers” since they are not immediately recognizable as having any particular significance. We did use comments to explain what they mean in the places where they are used, but using constants would have been better (e.g., UNSELECTED, RED_SELECTED, GREEN_SELECTED, etc.). Better still would be to use an enumeration (or enum), something that was suggested as an improvement to the code in the original lesson, too.

What do you think?

While neither the three-button approach used in the original lesson nor the potentiometer-plus-one-button approach used in this extension provides the ideal user experience, which do you think is better and why?

Lesson: Creating a Proximity Detector with an Arduino and an Ultrasonic Distance Sensor

By Robert Walsh

Engage

Most cars today have numerous sensors that help drivers detect obstacles that are nearby but potentially out of sight1. Often, they emit a series of audible tones that increase in pitch, frequency, or both as the car gets closer to the obstacle. In this lesson, we will create a similar system that uses light emitting diodes (LEDs) to indicate how far away an object is from a sensor. To complete the activity, you will need:

  • An Arduino Mega 2560 (other models will also work, but the pin numbers will be different)
  • One green, one yellow, and one red LED
  • Three 330 Ohm resistors
  • One HC-SR04 ultrasonic distance sensor
  • A breadboard
  • Several male-to-male jumper wires
  • Several male-to-female jumper wires (optional)
  • A computer and USB cable to program the Arduino

Note: All of the components needed for this lesson (except the computer) are available in the ELEGOO Mega 2560 Starter Kit available from Amazon.

Explore

Connecting the HC-SR04 Ultrasonic Distance Sensor

We are going to start by creating a simple circuit that incorporates just the HC-SR04, then we will add the LEDs in a second step. The HC-SR04 is very easy to control; it needs just four connections to the Arduino. Here is the wiring diagram:

Wiring diagram for the HC-SR04 ultrasonic distance sensor

We first need to connect the common power rail on the breadboard to a 5V pin on the Arduino. We also need to connect the common ground rail to a GND (ground) pin. The HC-SR04 has just four pins. VCC connects to common power, and GND connects to common ground. The trigger (TRIG) pin connects to output pin on the Arduino (I used pin 7), and the echo pin connects to an input pin.

Instead of inserting the HC-SR04 directly into the breadboard, it could be connected to the breadboard using male-to-female jumper wires. Then, by holding the sensor with its edges between thumb and forefinger, it can be more easily moved around. This would be a better configuration if moving the sensor is easier than moving the obstacles.

Here is the Arduino sketch for this circuit:

const int TRIGGER_PIN = 7;
const int ECHO_PIN = 6;

const float SPEED_OF_SOUND_IN_METERS_PER_SECOND = 340.0; /* define speed of sound as a float to avoid truncation with integer math */
const long ONE_MILLION_MICROSECONDS_PER_SECOND = 1000000;
const int ONE_HUNDRED_CM_PER_METER = 100;

void setup() {
  pinMode(TRIGGER_PIN, OUTPUT); /* set the trigger pin as an output pin */
  pinMode(ECHO_PIN, INPUT); /* set the echo pin as an input pin */

  Serial.begin(9600);
}

void loop() {
  /* Ensure the trigger pin is LOW */
  digitalWrite(TRIGGER_PIN, LOW);
  delayMicroseconds(2);

  /* Set the trigger pin HIGH for 10 microseconds */
  digitalWrite(TRIGGER_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIGGER_PIN, LOW);

  /* Measure the time it takes (in microseconds) for the echo pin to go HIGH */
  float duration = pulseIn(ECHO_PIN, HIGH);
  
  /* Convert the time to distance in meters based on the speed of sound */
  float distanceInMeters = duration * (SPEED_OF_SOUND_IN_METERS_PER_SECOND / ONE_MILLION_MICROSECONDS_PER_SECOND) / 2

  /* Convert meters to centimeters */
  float distanceInCentimeters = distanceInMeters * ONE_HUNDRED_CM_PER_METER;

  /* Print the distance to the serial console */
  Serial.print("Distance: ");
  Serial.print(distanceInCentimeters);
  Serial.println(" cm");  
}

When the trigger pin on the HC-SR04 is set high for 10 microseconds (ten millionths of a second), it sends out a series of sound waves at a frequency that is higher than humans can hear. This range is referred to as ultrasonic. The HC-SR04 also starts listening on for the sound waves to bounce back. When they are detected, the HC-SR04 sets the echo pin high. Since sound travels at a constant speed, by measuring the time it takes for the echo pin to go high, we can calculate the distance to whatever obstacle caused the sound to change direction. We have to divide the distance in half since the time measured was for the sound pulses to travel from the sensor to the obstacle and back.

When the sketch is compiled and uploaded to the Arduino, it should begin printing distance readings to the serial monitor window in the Arduino IDE. (If the serial monitor window is not open, use Serial Monitor from the Tools menu to open it.). Moving an obstacle close to the sensor should decrease the distance readings, and moving the obstacle away from the sensor should increase them. The maximum range is around 4 meters (about 13 feet), and the minimum range is 2 cm2.

Adding the LEDs

When driving, it would be inconvenient, distracting, and potentially dangerous to have to constantly read the distance from some kind of a text-based display. It would be better to have some kind of visual or auditory warning that could signal the driver and indicate the threat level. We are going to add LEDs to our circuit. Based on the distance to the obstacle, we will adjust the color and brightness of the LEDs. Here is the wiring diagram for the completed circuit:

Wiring diagram for the HC-SR04 ultrasonic distance sensor and the three LEDs

The cathode (negative leg) of each of the three LEDs is connected to ground, and the anode (positive leg) of each connects to a 330 Ohm resistor which is then connected to an output pin on the Arduino (I used pin 4 for red, pin 3 for yellow, and pin 2 for green). The pins for the LEDs must support pulse width modulation (PWM) since we will be using analogWrite to vary the brightness. When the obstacle gets within 30 cm (about 1 foot) of the sensor, the green LED will light up. As it gets closer, the brightness of the green LED will increase. At 20 cm, the yellow LED will light up and get brighter until the obstacle gets to 10 cm when the red LED will light up. Here is the Arduino sketch:

const int TRIGGER_PIN = 7;
const int ECHO_PIN = 6;

const int RED_LED = 4;
const int YELLOW_LED = 3;
const int GREEN_LED = 2;

const int OFF = 0;
const int DIM = 256 / 4;
const int HALF = 256 / 2;
const int FULL = 255;

const float SPEED_OF_SOUND_IN_METERS_PER_SECOND = 340.0; /* define speed of sound as a float to avoid truncation with integer math */
const long ONE_MILLION_MICROSECONDS_PER_SECOND = 1000000;
const int ONE_HUNDRED_CM_PER_METER = 100;

void setup() {
  pinMode(TRIGGER_PIN, OUTPUT); /* set the trigger pin as an output pin */
  pinMode(ECHO_PIN, INPUT); /* set the echo pin as an input pin */

  pinMode(RED_LED, OUTPUT); /* set the pin for the red led as an output pin */
  pinMode(YELLOW_LED, OUTPUT); /* set the pin for the yellow led as an output pin */
  pinMode(GREEN_LED, OUTPUT); /* set the pin for the green led as an output pin */

  digitalWrite(RED_LED, OFF); /* ensure the red led is off */
  digitalWrite(YELLOW_LED, OFF); /* ensure the yellow led is off */
  digitalWrite(GREEN_LED, OFF); /* ensure the green led is off */
  
  Serial.begin(9600);
}

int determineLedState(float distance, int distanceThreshold)
{
  /* Determine whether (and how bright) an LED should be lit based on distance */

  /* Assume the LED should be off */
  int state = OFF;

  /* If the distance is less than the thresholod, set the state to DIM (ex., < 30 for green) */
  if (distance <= distanceThreshold)
  {
    state = DIM;
  }

  /* If the distance is between the threshold and three less than the threshold, set the state to HALF (ex., < 27 for green) */
  if (distance <= (distanceThreshold - 3))
  {
    state = HALF;
  }

  /* If the distance is between the threshold and three less than the threshold, set the state to FULL (ex., < 24 for green) */
  if (distance <= (distanceThreshold - 6))
  {
    state = FULL;
  }
  
  return state;
}

void setLeds(float distance)
{
  /* Gets and sets the state for each LED based on distance */

  /* Determine what state each LED should have */
  int greenLedState = determineLedState(distance, 30);
  int yellowLedState = determineLedState(distance, 20);
  int redLedState = determineLedState(distance, 10);

  /* Write the states to the LEDs */
  analogWrite(GREEN_LED, greenLedState);
  analogWrite(YELLOW_LED, yellowLedState);
  analogWrite(RED_LED, redLedState);    
}

void loop() {
  /* Ensure the trigger pin is LOW */
  digitalWrite(TRIGGER_PIN, LOW);
  delayMicroseconds(2);

  /* Set the trigger pin HIGH for 10 microseconds */
  digitalWrite(TRIGGER_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIGGER_PIN, LOW);

  /* Measure the time it takes (in microseconds) for the echo pin to go HIGH */
  float duration = pulseIn(ECHO_PIN, HIGH);
  
  /* Convert the time to distance in meters based on the speed of sound */
  float distanceInMeters = duration * (SPEED_OF_SOUND_IN_METERS_PER_SECOND / ONE_MILLION_MICROSECONDS_PER_SECOND) / 2

  /* Convert meters to centimeters */
  float distanceInCentimeters = distanceInMeters * ONE_HUNDRED_CM_PER_METER;

  /* Set the states for the LEDs based on the distance */
  setLeds(distanceInCentimeters);

  /* Print the distance to the serial console */
  Serial.print("Distance: ");
  Serial.print(distanceInCentimeters);
  Serial.println(" cm");  
}

We added two functions to do the work of determining which LEDs should be lit and how bright each should be. Most of this is done in determineLedState. This function takes two parameters, the distance and the threshold at which the LED should be lit. For example, we want the green LED to light up when the object is 30 cm or less from the sensor. The function first assumes the LED will be off. However, if the distance to the obstacle is less than or equal to the threshold, the LED should be lit, and we need to determine how brightly. Again, using green as the example, the LED should be dim (1/4 brightness) if the object is less than 30 but more than 27 cm from the sensor; it should be lit to half its maximum brightness if the object is less than 27 and more than 24 cm; and it should be fully bright is the object is closer than 25 cm (<= 24). For the other LEDs, we simply use different threshold values. This logic means that the green LED will be fully bright anytime the yellow or red LED is also lit.

Compile and upload the sketch, then experiment by moving objects towards and away from the sensor (or moving the sensor towards or away from objects). As the object gets within 30 cm, the LEDs should come on, and they should get brighter as the object gets closer. Feel free to adjust the threshold distances and modifiers to make the LEDs more responsive for your environment.

Explain

Using reflected sound to detect obstacles is called echolocation3. Several animal species, including bats and dolphins, use echolocation to move safely and hunt effectively. The same principle is used in radar (an acronym that stands for radio detection and ranging)4 and sonar (sound navigation and ranging)5.

Modern cars include various kinds sensors, including ultrasonic sensors, that are capable of measuring the distance to nearby obstacles1. Combining these sensors with sounds, lights, and cameras allows the car to notify the driver when it is in danger of striking an obstacle. Other features like cruise control, self-parking, and even driverless cars use data from the sensors to make decisions about whether to slow down, stop, or turn. The HC-SR04 ultrasonic distance sensor is a common component of robot car kits that enables the robot to detect objects in its environment and to navigate around them.

Extend

Our code assumes that the speed of sound is a constant 340 meters per second. More accurately, though, the speed of sound varies with the air temperature6. Our distance sensor could be made more accurate if we incorporated a second sensor to measure the air temperature and to then calculate the speed of sound accordingly. Additionally, we could add multiple LEDs in each color and have the number that are lit help to indicate the distance to the detected object.

While colored lights at varying brightnesses make be appropriate warning indicators for some environments, using sounds might be better in others. Cars often use tones that vary in pitch and frequency based on the distance to the obstacle. For example, a high pitch sound that repeats at a fast rate indicates that we are very close to the object, while a slower, lower sound tells us we are farther away. A piezo buzzer7 could be incorporated into the circuit to generate tones based on the distance to the detected object. A camera, too, could be incorporated; however, this might be easier using a Raspberry Pi than an Arduino.

Evaluate

What we learned in this lesson:

  • How to use the HC-SR04 ultrasonic distance sensor to measure the distance to an object
  • How to calculate distance using the time required for a sound wave to reflect off an object and travel back to its source
  • How to use pulse width modulation to vary the brightness of an LED

How did you do with this lesson?

  • What parts were easy and what parts were confusing?
  • Were any parts a review of things you already knew?
  • What would you like to know more about?

References

1Stevenson, J. (2016, May 4). What are car proximity sensors and how do they work? Here360. https://360.here.com/2016/05/04/what-are-car-proximity-sensors-and-how-do-they-work/

2Elec Freaks. (n.d.). Ultrasonic ranging module HC-SR04 [Datasheet]. https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf

3Price, J. (n.d.). What is echolocation and which animals use it? Discover Wildlife. https://www.discoverwildlife.com/animal-facts/mammals/what-is-echolocation/

4Rouse, M. (n.d.). Radar (radio detection and ranging). Tech Target. https://searchmobilecomputing.techtarget.com/definition/radar

5National Oceanic and Atmospheric Administration. (n.d.). What is sonar? https://oceanservice.noaa.gov/facts/sonar.html

6NDT Resource Center. (n.d.). Temperature and the speed of sound. https://www.nde-ed.org/EducationResources/HighSchool/Sound/tempandspeed.htm

7Adafruit. (2020, August 5). Overview: Using piezo buzzers with CircuitPython and Arduino. https://learn.adafruit.com/using-piezo-buzzers-with-circuitpython-arduino

Lesson: Exploring Color with an Arduino and an RGB LED

By Robert Walsh

Engage

Have you ever wondered how we see color? Or how our TVs, phones, tablets, and computers can display such vivid and detailed pictures? In this lesson, we will use an Arduino to control red, green, and blue light emitting diodes (LEDs) to create a whole spectrum of colors, and then we will see how the concepts are applied in LED displays. To complete this activity, you will need:

  • An Arduino Mega 2560 (other models will also work, but the pin numbers will be different)
  • One red, one blue, and one green LED
  • One common cathode RGB LED
  • Three 220 Ohm or 330 Ohm resistors
  • Three push buttons
  • Three 5K Ohm or 10K Ohm resistors
  • A breadboard
  • Several male-to-male jumper wires
  • A computer and USB cable to program the Arduino

Note: All of the components needed for this lesson (except the computer) are available in the ELEGOO Mega 2560 Starter Kit available from Amazon.

Explore

The finished product will have three buttons, each of which will gradually increase the brightness of one of the colored LEDs as well as the corresponding component in the RGB LED. This will let us observe how combining differing amounts of red, green, and blue results in light that our eyes perceive as various shades of color.

We are going to build the circuit and write the sketch for this lesson in several small parts. There is essentially one small circuit that will be repeated for each of the three colors.

Controlling a Red LED

The first circuit will control the red LED. Here is the wiring diagram:

Wiring diagram for the portion of the circuit that will control the red LED

For this circuit, we have connected the common ground rail on the breadboard to a GND (ground) pin on the Arduino, and we have connected the common power rail to a 5V pin. The cathode (negative leg) of the red LED is connected to ground. The anode (positive leg) is connected to a 220 Ohm (or 330 Ohm) resistor. The other end of the resistor is connected to both a signal pin on the Arduino (I used pin 12) and to the red leg of the RGB LED. With the RGB LED held so that the longest leg (typically the common cathode) is second from the left, the red leg should be the first leg from the left. The common cathode of the RGB LED is connected to ground.

The button is inserted into the breadboard across the center channel. One leg is connected to the common power rail, while the other connects both to a signal pin on the Arduino (I used pin 4) and to a 5K Ohm (or 10K Ohm) resistor that is connected to ground. This is called a pull down resistor.

Which signal pin you choose for the LED is not important except that it must be capable of pulse width modulation (PWM) output. This is how we will eventually control the brightness of the LED.

Here is the Arduino sketch for this circuit:

const int RED_LED = 12; /* the signal pin to which the red LED anode is connected */
const int RED_BTN = 4; /* the signal pin to which the button for red is connected */

const int OFF = 0; /* the value for the LED when it is off */
const int ON = 1; /* the value for the LED when it is on */

boolean redPressed = false; /* indicates whether the button for red is currently pressed */
int redState = OFF; /* indicates the current state of the red LED */

void setup()
{
    pinMode(RED_LED, OUTPUT); /* set the red LED pin to output */
    pinMode(RED_BTN, INPUT); /* set the pin for the button to control the red LED to input */

    digitalWrite(RED_LED, OFF); /* ensure the red LED is off */
}

boolean debounce(int whichButton, boolean lastState)
{
    /* 
     * The debounce function ensures the button state has 
     * had time to "settle" before returning whether 
     * it is pressed 
     */

    boolean currentState = digitalRead(whichButton); /* get the state of the button */

    if (currentState != lastState) /* if the new state is different from the last */
    {
        delay(5); /* wait 5 milliseconds and read the state again */
        currentState = digitalRead(whichButton);
    }
    return currentState;
}

int nextState(int currentState)
{
    /*
     * The nextState function determines the new state
     * for the LED based on its current state
     */

    if (currentState == OFF) /* if the LED is currently off */
    {
        return ON; /* its next state should be on */
    }

    return OFF; /* otherwise, it is on and should be off */
}

void loop()
{
    boolean isPressed = debounce(RED_BTN, redPressed); /* check to see if the button for the red LED is pressed */
    if (isPressed && !redPressed) /* if the button is pressed now but was not pressed before */
    {
        redState = nextState(redState); /* get the new state for the red LED */
    }
    redPressed = isPressed; /* save the pressed state of the button for the red LED */

    digitalWrite(RED_LED, redState);
}


Compile the sketch and upload it to the Arduino. The red LED and the RGB LED should be off. Pressing the button should turn on the red LED and cause the RGB LED to light up red. Pressing the button again should turn both LEDs off.

Gradually Increasing the Brightness

So far, we are only turning the LEDs on and off. It would be more interesting if we could gradually increase the brightness so that the lights behave like a 3-way light bulb. To do that, we need to make some changes to the sketch. Specifically, we need to add more states than just off and on, and we will need to use pulse width modulation (PWM) to vary the voltage going to the circuit. Here is the new sketch with the changes highlighted:

const int RED_LED = 12; /* the signal pin to which the red LED anode is connected */
const int RED_BTN = 4; /* the signal pin to which the button for red is connected */

const int OFF = 0; /* the value for the LED when it is off */
const int DIM = 256 / 4; /* the value for the LED when it is 1/4 bright */
const int HALF = 256 / 2; /* the value for the LED when it is 1/2 bright */
const int SEMI = (256 / 4) * 3; /* the value for the LED when it is 3/4 bright */
const int FULL = 255; /* the value for the LED when it is fully lit */

boolean redPressed = false; /* indicates whether the button for red is currently pressed */
int redState = OFF; /* indicates the current state of the red LED */

void setup()
{
    pinMode(RED_LED, OUTPUT); /* set the red LED pin to output */
    pinMode(RED_BTN, INPUT); /* set the pin for the button to control the red LED to input */

    digitalWrite(RED_LED, OFF); /* ensure the red LED is off */
}

boolean debounce(int whichButton, boolean lastState)
{
    /* 
     * The debounce function ensures the button state has 
     * had time to "settle" before returning whether 
     * it is pressed 
     */

    boolean currentState = digitalRead(whichButton); /* get the state of the button */

    if (currentState != lastState) /* if the new state is different from the last */
    {
        delay(5); /* wait 5 milliseconds and read the state again */
        currentState = digitalRead(whichButton);
    }
    return currentState;
}

int nextState(int currentState)
{
    /*
     * The nextState function determines the new state
     * for the LED based on its current state
     */

    if (currentState == OFF) /* if the LED is currently off */
    {
        return DIM; /* its next state should be on */
    }

    if (currentState == DIM) /* if the LED is currently dim */
    {
        return HALF; /* its next state should be half */
    }

    if (currentState == HALF) */ if the LED is currently half */
    {
        return SEMI; /* its next state should be 3/4 */
    }

    if (currentState == SEMI) /* if the LED is currently 3/4 */
    {
        return FULL; /* its next state should be full */
    }

    return OFF; /* otherwise, it is already fully lit and should next be off */
}

void loop()
{
    boolean isPressed = debounce(RED_BTN, redPressed); /* check to see if the button for the red LED is pressed */
    if (isPressed && !redPressed) /* if the button is pressed now but was not pressed before */
    {
        redState = nextState(redState); /* get the new state for the red LED */
    }
    redPressed = isPressed; /* save the pressed state of the button for the red LED */

    analogWrite(RED_LED, redState);
}


Once this sketch is compiled and uploaded, pressing the button should cycle the red LED and the RGB LED through five different states, each with a different brightness.

Controlling a Green LED

Now that we can control the red LED, let’s add a green one. We are going to replicate the existing circuit by adding a green LED and button to control it. Here is the new wiring diagram:

Wiring diagram for the portion of the circuit that will control the red and green LEDs

The cathode of the green LED is connected to ground, and the anode is connected to a 220 Ohm or 330 Ohm resistor. The other end of the resistor connects both to the green leg of the RGB LED and to a signal pin on the Arduino (I used pin 11). Again, which signal pin is not important so long as it is capable of PWM. The green leg of the LED is typically to the right of the common cathode. The button is the same as for the red LED except that it is connected to a different signal pin on the Arduino (I used pin 3).

We also need to make some changes to the sketch:

const int RED_LED = 12; /* the signal pin to which the red LED anode is connected */
const int RED_BTN = 4; /* the signal pin to which the button for red is connected */

const int GREEN_LED = 11; /* the signal pin to which the green LED anode is connected */
const int GREEN_BTN = 3; /* the signal pin to which the button for green is connected */

const int OFF = 0; /* the value for the LED when it is off */
const int DIM = 256 / 4; /* the value for the LED when it is 1/4 bright */
const int HALF = 256 / 2; /* the value for the LED when it is 1/2 bright */
const int SEMI = (256 / 4) * 3; /* the value for the LED when it is 3/4 bright */
const int FULL = 255; /* the value for the LED when it is fully lit */

boolean redPressed = false; /* indicates whether the button for red is currently pressed */
int redState = OFF; /* indicates the current state of the red LED */

boolean greenPressed = false; /* indicates whether the button for green is currently pressed */
int greenState = OFF; /* indicates the current state of the green LED */

void setup()
{
    pinMode(RED_LED, OUTPUT); /* set the red LED pin to output */
    pinMode(RED_BTN, INPUT); /* set the pin for the button to control the red LED to input */

    pinMode(GREEN_LED, OUTPUT); /* set the green LED pin to output */
    pinMode(GREEN_BTN, INPUT); /* set the pin for the button to control the green LED to input */

    digitalWrite(RED_LED, OFF); /* ensure the red LED is off */
    digitalWrite(GREEN_LED, OFF); /* ensure the green LED is off */
}

boolean debounce(int whichButton, boolean lastState)
{
    /* 
     * The debounce function ensures the button state has 
     * had time to "settle" before returning whether 
     * it is pressed 
     */

    boolean currentState = digitalRead(whichButton); /* get the state of the button */

    if (currentState != lastState) /* if the new state is different from the last */
    {
        delay(5); /* wait 5 milliseconds and read the state again */
        currentState = digitalRead(whichButton);
    }
    return currentState;
}

int nextState(int currentState)
{
    /*
     * The nextState function determines the new state
     * for the LED based on its current state
     */

    if (currentState == OFF) /* if the LED is currently off */
    {
        return DIM; /* its next state should be on */
    }

    if (currentState == DIM) /* if the LED is currently dim */
    {
        return HALF; /* its next state should be half */
    }

    if (currentState == HALF) */ if the LED is currently half */
    {
        return SEMI; /* its next state should be 3/4 */
    }

    if (currentState == SEMI) /* if the LED is currently 3/4 */
    {
        return FULL; /* its next state should be full */
    }

    return OFF; /* otherwise, it is already fully lit and should next be off */
}

void loop()
{
    boolean isPressed = debounce(RED_BTN, redPressed); /* check to see if the button for the red LED is pressed */
    if (isPressed && !redPressed) /* if the button is pressed now but was not pressed before */
    {
        redState = nextState(redState); /* get the new state for the red LED */
    }
    redPressed = isPressed; /* save the pressed state of the button for the red LED */

    isPressed = debounce(GREEN_BTN, greenPressed); /* check to see if the button for the green LED is pressed */
    if (isPressed && !greenPressed) /* if the button is pressed now but was not pressed before */
    {
        greenState = nextState(greenState); /* get the new state for the green LED */
    }
    greenPressed = isPressed; /* save the pressed state of the button for the green LED */

    analogWrite(RED_LED, redState);
    analogWrite(GREEN_LED, greenState);
}


We simply added constants and variables to represent the state of the green LED and the button that controls it. Then we replicated the behavior that controls the red LED so that it works for the green one, too. Once the code is compiled and uploaded, pressing the button for red will increase the brightness on the red LED, and pressing the button for green will control the green LED. The interesting part, though, is that when both the red and green LEDs are lit, the RGB LED is no longer either red or green but a combination of both!

Controlling a Blue LED

The final step is to replicate the circuit once more so that we can control a blue LED. Here is the wiring diagram:

Wiring diagram for the completed circuit that will control the all three LEDs

The breadboard may be starting to get a little bit crowded, but the new components should be familiar. The cathode of the blue LED is connected to ground, and the anode is connected to a 220 Ohm or 330 Ohm resistor. The other end of the resistor connects to the blue leg of the RGB LED and to a signal pin (capable of PWM) on the Arduino (I used pin 10). The button is the same as for the buttons for red and green, but it should be connected to a different signal pin (I used pin 2).

The new sketch is here:

const int RED_LED = 12; /* the signal pin to which the red LED anode is connected */
const int RED_BTN = 4; /* the signal pin to which the button for red is connected */

const int GREEN_LED = 11; /* the signal pin to which the green LED anode is connected */
const int GREEN_BTN = 3; /* the signal pin to which the button for green is connected */

const int BLUE_LED = 10; /* the signal pin to which the blue LED anode is connected */
const int BLUE_BTN = 2; /* the signal pin to which the button for blue is connected */

const int OFF = 0; /* the value for the LED when it is off */
const int DIM = 256 / 4; /* the value for the LED when it is 1/4 bright */
const int HALF = 256 / 2; /* the value for the LED when it is 1/2 bright */
const int SEMI = (256 / 4) * 3; /* the value for the LED when it is 3/4 bright */
const int FULL = 255; /* the value for the LED when it is fully lit */

boolean redPressed = false; /* indicates whether the button for red is currently pressed */
int redState = OFF; /* indicates the current state of the red LED */

boolean greenPressed = false; /* indicates whether the button for green is currently pressed */
int greenState = OFF; /* indicates the current state of the green LED */

boolean bluePressed = false; /* indicates whether the button for blue is currently pressed */
int blueState = OFF; /* indicates the current state of the blue LED */

void setup()
{
    pinMode(RED_LED, OUTPUT); /* set the red LED pin to output */
    pinMode(RED_BTN, INPUT); /* set the pin for the button to control the red LED to input */

    pinMode(GREEN_LED, OUTPUT); /* set the green LED pin to output */
    pinMode(GREEN_BTN, INPUT); /* set the pin for the button to control the green LED to input */

    pinMode(BLUE_LED, OUTPUT); /* set the blue LED pin to output */
    pinMode(BLUE_BTN, INPUT); /* set the pin for the button to control the blue LED to input */

    digitalWrite(RED_LED, OFF); /* ensure the red LED is off */
    digitalWrite(GREEN_LED, OFF); /* ensure the green LED is off */
    digitalWrite(BLUE_LED, OFF); /* ensure the blue LED is off */
}

boolean debounce(int whichButton, boolean lastState)
{
    /* 
     * The debounce function ensures the button state has 
     * had time to "settle" before returning whether 
     * it is pressed 
     */

    boolean currentState = digitalRead(whichButton); /* get the state of the button */

    if (currentState != lastState) /* if the new state is different from the last */
    {
        delay(5); /* wait 5 milliseconds and read the state again */
        currentState = digitalRead(whichButton);
    }
    return currentState;
}

int nextState(int currentState)
{
    /*
     * The nextState function determines the new state
     * for the LED based on its current state
     */

    if (currentState == OFF) /* if the LED is currently off */
    {
        return DIM; /* its next state should be on */
    }

    if (currentState == DIM) /* if the LED is currently dim */
    {
        return HALF; /* its next state should be half */
    }

    if (currentState == HALF) */ if the LED is currently half */
    {
        return SEMI; /* its next state should be 3/4 */
    }

    if (currentState == SEMI) /* if the LED is currently 3/4 */
    {
        return FULL; /* its next state should be full */
    }

    return OFF; /* otherwise, it is already fully lit and should next be off */
}

void loop()
{
    boolean isPressed = debounce(RED_BTN, redPressed); /* check to see if the button for the red LED is pressed */
    if (isPressed && !redPressed) /* if the button is pressed now but was not pressed before */
    {
        redState = nextState(redState); /* get the new state for the red LED */
    }
    redPressed = isPressed; /* save the pressed state of the button for the red LED */

    isPressed = debounce(GREEN_BTN, greenPressed); /* check to see if the button for the green LED is pressed */
    if (isPressed && !greenPressed) /* if the button is pressed now but was not pressed before */
    {
        greenState = nextState(greenState); /* get the new state for the green LED */
    }
    greenPressed = isPressed; /* save the pressed state of the button for the green LED */

    isPressed = debounce(BLUE_BTN, bluePressed); /* check to see if the button for the blue LED is pressed */
    if (isPressed && !bluePressed) /* if the button is pressed now but was not pressed before */
    {
        blueState = nextState(blueState); /* get the new state for the blue LED */
    }
    bluePressed = isPressed; /* save the pressed state of the button for the blue LED */

    analogWrite(RED_LED, redState);
    analogWrite(GREEN_LED, greenState);
    analogWrite(BLUE_LED, blueState);
}


Again, we simply replicated the code that handles the red and the green buttons and LEDs for blue. After compiling and uploading, you should be able to control all three LEDs independently, and the color of the RGB LED should depend on the values of each of the individual color LEDs.

Explain

Perceiving Color

What we perceive as color is light at different wavelengths1. Receptors in the eye stimulated by light at varying wavelengths and convert these stimuli into signals that the brain perceives as colors2. Combining different amounts of red, green, and blue light can produce the full spectrum2.

LED computer displays and televisions use this same technique to produce high-resolution images3. These displays are composed of many (over 8 million for a 4K TV) RGB LEDs that can be lit individually. By controlling the red, green, and blue values of these picture elements (called pixels), the display can represent images. The more pixels that are available, typically referred to as the resolution of the display, the better the quality of the resulting image.

Pulse Width Modulation (PWM)

This lesson leverages a concept called pulse width modulation4 or PWM. In order to control the brightness of the LEDs, we need to somehow vary the amount of voltage sent to them. However, the Arduino is capable of setting the value of output pins to either high (5 volts) or low (0 volts). By controller how long the signal is high, though, we can fool the circuit into behaving as if it is getting less voltage. The effect is similar to flicking a light switch on and off very quickly. The light can only be on or off; there are no other possible values. If, though, the switch is flipped fast enough, it will look like it is dim. The ratio of the time spent on and the time spent off dictates how bright the light will appear. Similarly, if the Arduino toggles the state of the pin between high and low very quickly, the LED’s brightness can be controlled. Just as with the light switch, setting the pin high longer makes the LED appear brighter. The time spent on is called the duty cycle. In this lesson, we defined DIM to be 25% of the maximum value. When we use analogWrite to send that value to the signal pin, we are telling the Arduino to use a 25% duty cycle. In other words, the pin will be high 25% of the time and low 75% of the time. Only some of the Arduino’s pins are capable of PWM.

Pull Down Resistors

You probably noticed that we connected a high-value resistor to ground with each of the buttons. This is called a pull down resistor5. The reason for this is to ensure that there is a complete circuit even when the button is open (not pressed). One side of the button is connected to the common power rail on the breadboard. When the button is pressed, the switch is closed, and the signal pin reads high. This is because power is able to flow through the button. Without the resistor, when the button is not pressed, the signal pin would be connected to an open circuit, and the Arduino would not be able to accurately determine its state. By adding the resistor, even when the button is not pressed, there is a closed circuit from the signal bin to ground. Also, since current always follows the path of least resistance, when the button is pressed, there is less resistance through the switch to the power rail than through the resistor to the ground rail. Therefore, the Arduino will detect that the pin is high. In short, when the button is not pressed (and the switch is open), there is only one path: through the resistor to ground. When the button is pressed (and the switch is closed), there are two paths, so current will travel the path with less resistance which goes through the button to the power rail.

Debounce

When a button is initially pressed or released, its state may briefly vary between high and low before settling into the correct final state6. To deal with this, we use a technique called debouncing. That’s a fancy word to mean that we read the button’s state, and if we think it is changing, we wait a very short amount of time and read it again. In our code, the debounce function first reads the state of the button. Then, if the current state is different from the previous state, we wait (delay) for 5 milliseconds and read the state again. This gives us a reliable value for the button’s current state.

Extend

Improving the Code

The final sketch for this project is pretty clean. It uses constants to represent the pins and the various states for the LEDs rather than having “magic numbers” scattered throughout the code. It uses functions for determining whether a button is pressed and for calculating the next state of the LED. The functions are written so that they can be used for any of the buttons or LEDs rather than having to replicate the copy for each button and LED.

There are, though, some improvements that could be made. First, the nextState function could be rewritten to use a switch statement rather than a series of if statements. Switch is better when there are multiple outcomes that are mutually exclusive. Alternatively, an enum could be used to represent that states so that the next state could be determined by simply iterating over the set of possible values.

There is some duplication in the loop function for determining whether a button is pressed and then calculating the next state. This could be extracted into a function that returns a struct containing the next state and whether the button is pressed.

Improving the User Experience

It is great that we can set each of the colored LEDs to one of five possible values. This give us 125 possible colors for the RGB LED (5 red * 5 green * 5 blue). However, analogWrite can accept up to 256 values (where 0 is off and 255 is fully lit)! We could give our user more control over the lights if we added new brightness levels. If the user could set each individual LED to any value between 0 and 255, over 16 million colors would be possible! At some point, though, it would become difficult to get a specific color by having to cycle through more and more values with individual button presses. Changing the circuit to use potentiometers instead of buttons would provide a better mapping between the value of the control mechanism and the shade of the RGB LED. With buttons, we are using digital sensors to create analog values, but with potentiometers, we would be reading analog values directly from the controls. We could even use a single two-axis joystick to control the colors, but we would need to find a way to map two-dimensional coordinate values into a set of three possible color values between 0 and 255. A color wheel might help.

Color Blindness

Color blindness is an inability to see the full spectrum of color. It is caused when some of the color receptors in the eye (called the cones) do not work properly7. About 8% of men and 1% of women are affected by some form of color blindness1. Is there anything that you learned about color in this lesson that might help people who suffer from color blindness?

Evaluate

What we learned in this lesson:

  • How red, green, and blue can be combined to create the full spectrum of color
  • How to use pulse width modulation to vary the brightness of an LED
  • How to use a pull down resistor to ensure that a button’s state can be determined reliable when it is not pressed
  • How to use the debounce technique to eliminate spurious and potentially inaccurate readings of a button’s state while it is being pressed or released

How did you do with this lesson?

  • What parts were easy and what parts were confusing?
  • Were any parts a review of things you already knew?
  • What would you like to know more about?

References

1Science Learning Hub. (n.d.). Colours of light. https://www.sciencelearn.org.nz/resources/47-colours-of-light

2Pantone. (n.d.). How do we see color? An introduction to color an the human eye. https://www.pantone.com/articles/color-fundamentals/how-do-we-see-color

3Eizo. (n.d.). EIZO 4K monitors: High definition and large screen sizes. https://www.eizo.com/library/basics/eizo_4k_monitors/

4Heath, J. (2017, April 4). PWM: Pulse width modulation: What is it and how does it work? Analog IC Tips. https://www.analogictips.com/pulse-width-modulation-pwm/

5EE Power. (n.d.) Pull up resistor / pull down resistor. https://eepower.com/resistor-guide/resistor-applications/pull-up-resistor-pull-down-resistor

6Arduino. (n.d.). Debounce. https://www.arduino.cc/en/Tutorial/BuiltInExamples/Debounce

7Turbert, D. (2019, September 6). What is color blindness? American Academy of Ophthalmology. https://www.aao.org/eye-health/diseases/what-is-color-blindness

DIY Arcade Cabinet – Part 1

STEM should be fun, right? And what could be more fun than an arcade! When I was a kid, my parents were part of a bowling league, and when they had a match, they would give my brother and me some money so that we could play in the arcade while they bowled. So I decided it would be neat to build my own arcade cabinet powered by a Raspberry Pi computer and RetroPie software.

The project isn’t finished yet, but here are some pics of the work in progress.

Over the years, I have collected various bits and pieces from old computers, so I have reused an old 15″ LCD monitor and some speakers. I bought the arcade controls through Amazon.

For the most part, the system, can be completely controlled from the arcade joystick and buttons. However, some of the initial setup required a keyboard, so I added a wireless Logitech keyboard with a trackpad and a small drawer for easy access.

Lesson: Exploring the Magnetometer using the Raspberry Pi, the Sense HAT module, and Python

By: Robert Walsh

Engage

¿Que le dijo un imán a otro imán? Respuesta: Eres muy atractivo1

(What did one magnet tell another one? Answer: You are very attractive)

Does your smartphone or tablet have a compass app? Most do (or at least one is available if not installed by default). Have you ever used it? What kind of information does it provide? Have you ever wondered how it works?

Round grey and black compass

The compass app on your smartphone is intended to emulate (or pretend to be) a real compass like the one seen here. A compass uses a magnet to indicate which direction is north2. In this lesson, we will use a Raspberry Pi with the Sense HAT module to demonstrate how a compass works. To complete this activity, you will need:

Explore

The video below will walk you through creating a Python program that will point you in the right direction! Well, it will at least tell you which way is north. This is the same technology used in your smartphone to power the compass app.

Note: The download is a ZIP file and will have to be expanded to get to the Python source code. Python programs have an .py file extension.

Explain

The Sense HAT contains an inertial monitoring unit (IMU) that includes a component called a magnetometer3. “A magnetometer is a scientific instrument used to measure the strength and direction of the magnetic field in the vicinity of the instrument” (section 4.2.4, para. 1)4. The magnetometer translates its internal readings into a direction, so, just like a magnet, a magnetometer can tell us which way is north.

Evidence exists to suggest that humankind has understood the principles of magnetism since the very earliest recorded history. Compasses were first used to aid navigation beginning around 1000 or 1100 CE in China2. Today, compasses can be found in scouts’ backpacks as well as in airplanes and spacecraft. Some may be actual magnets while others use magnetometers just like the one in the Sense HAT module.

In our Python code, we queried the Sense HAT’s magnetometer, and it told us the direction of north measured in degrees. We then translated those degrees into XY coordinates to determine which pixels on the Sense HAT’s LED matrix to light up. As the Raspberry Pi and the Sense HAT are rotated, the magnetometer updates and our Python code redraws the display on the LED matrix so that the lit pixels are always point north.

Extend

Apple was perhaps the first company to incorporate the magnetometer into their smartphones in 2009. In October, 2020, Apple filed for a new patent describing how the magnetometer could be made to provide more accurate readings5.

Researchers have found other ways to improve the accuracy of magnetometers, particularly when they are used in environments that distort or interfere with the Earth’s natural magnetic fields. In one such innovation, multiple magnetometers are used, and their readings are combined using a complex mathematical formula. This technology is used in virtual reality video games and in a system used to assist patients when rehabilitating after certain kinds of injuries6.

The Python program could be extended to show the letters to indicate cardinal direction that the Raspberry Pi and Sense HAT module are facing. For example, If the magnetometer reads 45 degrees, then the device is facing north west. (In other words, the north is 45 degrees to the right or east of the direction the device is facing.). Because of the limited space available on the Sense HAT LED matrix, it might be necessary to cycle between the compass display and the cardinal direction. You could even add a third state that displays the compass heading in degrees.

The program could be further improved by introducing functions to encapsulate some of the lower-level steps. For example, coloring the pixel for north red actually involves also clearing the red color from the last pixel representing north. These two steps could be combined into a function called point_north.

How else could you think to use a magnetometer? What other changes might you make to the Python program we wrote in this lesson?

Evaluate

What we learned in this lesson:

  • How to interact with the Sense HAT module using Python 3 on a Raspberry Pi
  • How the magnetometer component of the IMU detects and reports which direction is north
  • How to locate an object on an XY coordinate plane
  • How to use Python lists (arrays), tuples, for loops, and while loops
  • How to use a proportion to convert from one set of units to another

How did you do with the lesson?

  • What parts were easy and what parts were confusing?
  • Were any parts a review of things you already knew?
  • What would you like to know more about?

References

1Arreguin-Anderson, M. G., & Ruiz-Escalante, J. A. (2018). Adivinanzas and dichos: Preparing prospective educators to teach science by incorporating culturally responsive tools. Journal of Latinos and Education, 17(1), 84-91. https://dx.doi.org/10.1080/15348431.2016.1257447

2National Geographic. (n.d.) Compass. https://www.nationalgeographic.org/encyclopedia/compass/

3Raspberry Pi Foundation. (n.d.). Sense HAT. https://www.raspberrypi.org/products/sense-hat/?resellerType=home

4Bai, Y., & Bai, Q. (2019). 4 – Subsea surveying, positioning, and foundation. In Y. Bai & Q. Bai (Eds.), Subsea Engineering Handbook (Second Edition) (pp. 81-121). Gulf Professional Publishing. https://doi.org/10.1016/B978-0-12-812622-6.00004-X

5Purcher, J. (2020, October 9). Apple reveals their new magnetometer architecture that will advance compass and mapping apps & future gaming. Patently Apple. https://www.patentlyapple.com/patently-apple/2020/10/apple-reveals-their-new-magnetometer-architecture-that-will-advance-compass-and-mapping-apps-future-gaming.html

6Whittmann, F., Lambercy, O., & Gassert, R. (2019). Magnetometer-based drift correction during rest in IMU arm motion tracking. Sensors, 19(6), 1312. https://dx.doi.org/10.3390%2Fs19061312

Lesson: Exploring the Gyroscope using the Raspberry Pi, the Sense HAT module, and Scratch

By: Robert Walsh

Engage

Have you ever played a video game where all you had to do was tilt the controller to direct the on-screen action? The Nintendo Wii was one of the first gaming systems to employ this technology when it was released in 20061, and smartphone and tablet games like Temple Run and Subway Surfers use it too. Did you stop playing the game long enough to wonder how this works?

In this lesson, we will use a Raspberry Pi with the Sense HAT module to create a simple scene where you just need to tilt the Raspberry Pi to control a rocket ship. To complete this activity, you will need:

Explore

The video below will walk you through creating a Scratch program that allows you to control a rocket ship in space using a Raspberry Pi with a Sense HAT module. This is the same technology used in smartphones and video game controllers.

If you need help getting your program working, try these downloads:

Note: The complete project download is a ZIP file and will have to be expanded to get to the Scratch program. Scratch programs have an .SB3 file extension.

Explain

The Sense HAT contains an inertial monitoring unit (IMU) that is able to detect motion2. One component of the IMU is a gyroscope that detects rotation. When the Sense HAT is tilted forward or backward, a value called roll changes. When it is tilted left or right, the pitch changes. The Sense HAT can also detect a twisting rotation (like if you left the Raspberry Pi flat on a surface and rotated it to the left or right without tilting it). This is called yaw. Our program did not use the yaw value.

Airplanes, spacecraft, and other flying objects use pitch, roll, and yaw to describe their motion3. Pitch generally indicates whether the object is climbing or diving, while roll typically the tilt to the left or the right. (The Sense HAT extension for Scratch, though, uses forward and backward for roll and left or right for pitch.). Yaw shows where the nose is pointed.

This project also taught us about the XY coordinate plane. We used XY coordinates to position our sprite and to indicate which of the Sense HAT’s LEDs we wanted lit. For the sprite, the origin (the center or the point whose X and Y coordinates are both zero) was located in the center of the stage. X values greater than zero were to the right of center, while those with values less than zero were to the left. Y values greater than zero were above the center, and those less than zero were below. To move the rocket to the right, we increased the X coordinate, and to move to the left, we decreased the X. Likewise, to go up, we increased the Y, and to go down, we decreased it.

The Sense HAT LED matrix uses XY coordinates, too, but the origin is not in the center. It is in the upper left corner. As we move to the right, the X coordinate gets larger. However, as we move down, the Y coordinate gets larger. This is the opposite of the coordinate system for the sprite where larger Y values were higher on the screen. Another important point about the coordinates for the LEDs on the Sense HAT is that the numbering starts at zero, not one. For example, an LED in the first column has an X coordinate of 0 and one in the last column has an X coordinate of 7 even though there are 8 columns. The same is true for the Y coordinates, but instead of columns, the Y indicates the row.

Extend

In addition to game controllers and flying crafts, gyroscopes and IMUs are used in other devices, too. Segways, innovative personal transportation devices sometimes used by police officers and security guards in places where other vehicles are impractical, also use internal gyroscopes to help keep the Segway upright and to detect which way the rider is leaning4.

There are many ways that we could extend the rocket ship program. For example, we could make the ship move faster in a given direction based on how long the Sense HAT is tilted. This would make the movement more realistic. We could also use the yaw value to turn the ship rather than the left or right tilt. This might be a more appropriate control system game with a car or even a person. A game viewed in the first-person where the player sees what the character sees could use both tilt and yaw. One could turn the character, while the other could just turn the head to look in a different direction.

Another change could involve adding obstacles that the player must avoid. Or the player could be expected to collect the other objects on the screen rather than to avoid them.

What other adaptations could you think to make?

Evaluate

What we learned in this lesson:

  • How to interact with the Sense HAT module using Scratch 3 on a Raspberry Pi
  • How the gyroscope component of the IMU detects motion and reports pitch and roll
  • How to locate an object on an XY coordinate plane

How did you do with the lesson?

  • What parts were easy and what parts were confusing?
  • Were any parts a review of things you already knew?
  • What would you like to know more about?

References

1Romero, J. (2006, December 18). How do motion-sensing video game controllers work? ScienceLine. https://scienceline.org/2006/12/motioncontrollers/

2Raspberry Pi Foundation. (n.d.). Getting started with the Sense HAT. https://projects.raspberrypi.org/en/projects/getting-started-with-the-sense-hat/8

3SoftwarePole. (2015, March 25). Airplane control – roll, pitch, yaw [YouTube video]. https://www.youtube.com/watch?v=pQ24NtnaLl8

4Harris, T. (n.d.). How Segways work. How Stuff Works. https://science.howstuffworks.com/transport/engines-equipment/ginger.htm

Technological fluency, not computer literacy

By: Robert Walsh

I graduated high school in 1988, and personal computers were only just beginning to invade the home. Phones still had cords that tethered them to the wall, and many had rotary dials instead of push buttons. A digital watch was the height of mobile technology, particularly if it had alarms, could keep time in multiple time zones, or sported a mini calculator. My generation was on the leading edge of the transition from a world where computers were used only for specialized purposes to one where they have become a ubiquitous part of everyday life. As such, it was important that people in that time learned how to use computers to help with routine tasks. It was less important that they knew much about how they worked. Knowing how to use a word processor or a spreadsheet provided an advantage over others who did not have those skills.

It is no longer 1988, however, and computers are everywhere. More than 50% of children in the United States have their own smartphones by the time they are 11 years old, and over 80% of teenagers have them1. They know how to use them, too. I’ve seen kids record a video, edit it, and publish it to YouTube, Instagram, Facebook, or some other social media platform using only their cell phones. In this age, typewriters and calculators have been rendered almost obsolete, and being able to use a word processor or a spreadsheet is a life skill, not a competitive advantage.

Marina Bers describes this as the difference between being computer literate and technologically fluent2. She explains that computer literacy is the ability to use a computer and the programs running on it to accomplish specific tasks. Technological fluency, though, is the ability to use a computer “in a creative and personally meaningful way” (p. 14) in the same way that someone fluent in a foreign language can communicate “effortlessly and smoothly” (p. 14).

As we work to provide STEM education opportunities, we must strive to create technological fluency, not simply computer literacy. Students need to understand how technology works, not just how to use it. One who knows only how to use computers to do things others have made possible will always be dependent, while those who grasp the inner workings will be able to extend that which exists and innovate to create that which will be needed.

1Kamenz, A. (2019, October 31). It’s a smartphone life: More than half of U.S. children now have one. NPR. https://www.npr.org/2019/10/31/774838891/its-a-smartphone-life-more-than-half-of-u-s-children-now-have-one

2Bers M. U. (2010). Beyond computer literacy: supporting youth’s positive development through technology. New directions for youth development2010(128), 13–23. https://doi.org/10.1002/yd.371