© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
C. BellBeginning MicroPython with the Raspberry Pi Picohttps://doi.org/10.1007/978-1-4842-8135-2_10

10. Project: Sound Activated Lights

Charles Bell1  
(1)
Warsaw, VA, USA
 

As we saw in Chapter 9, the Grove component system can help make your hardware connections much simpler with less risk of incorrectly wiring your components. Indeed, except for making sure you are plugging in the Grove cables to the correct ports, you can’t make a wrong or reversed connection.

The Grove modules are all self-contained boards that have everything the main component needs, so there is no need to wire up additional hardware like resistors. The challenge lies in writing your code to talk to the components. Most Grove modules have examples you can use, and some have libraries you can download. However, some do not have any Python examples, or the Python examples are written for the Raspberry Pi rather than the Pico. In those cases, you may need to either write the code yourself using the examples as a guide or modify existing Python libraries for use on the Pico.

Since this is our first Grove project, in this chapter we will keep those issues to a minimum and instead spend some time looking at the Grove hardware and how to connect things to our Pico.

Overview

In this chapter, we begin our tour of example Grove projects with a simple project that demonstrates how to use a sound sensor and an RGB LED to display assorted colors based on the sound detected. The idea is the LED will light up whenever sound is detected, and the color will differ based on the loudness of the sound. So, we will be creating a sound detector.

The code for this project will need to read from an analog sensor (the sound sensor) and convert that value to a range of red, green, and blue values to convert the integer that the analog-to-digital converter returns from the sensor. As you will see, there is a bit of trickery needed to ensure the higher the value of the sensor, the brighter (higher values) the RGB values. We use those values to turn on an RGB LED.

Now let’s see what components are needed for this project, and then we will see how to wire everything together.

Required Components

The components for this project include a Grove host board that you can plug your Pico into that supports multiple Grove connectors. Recall, Grove connectors on the host board support one of several protocols including analog, digital, and I2C. We will need two Grove modules: a sound sensor and an RGB LED.

Table 10-1 lists the components you will need in addition to your Pico and USB cable. Links to vendors are provided should you want to purchase the components.
Note

Each Grove module comes with a short cable. If you need longer cables, see the link in the table for options.

You can purchase the components directly from Seeed Studio via the links in the table, or you can often find them at online retailers such as Adafruit (adafruit.com), SparkFun (sparkfun.com), or any electronics store that carries electronic components. Costs shown are estimates and do not include any shipping costs.

Now, let’s discuss the new components we will be using.

Grove Shield for Pi Pico

We saw the Grove Shield for Pico in Chapter 9, but let’s explore its features in more detail. There are ten Grove connectors that include three analog ports, three digital ports, two UART ports, and two I2C ports. There is also an SPI header and a switch that allows you to choose between 5V and 3.3V to power the Grove connectors. Figure 10-1 shows the Grove Shield for Pico.
Figure 10-1

Grove Shield for Pi Pico v1.0 (courtesy of seeedstudio.com)

Notice the 5V/3.3V switch is located in the upper left, and the SPI breakout is located in the lower right. Notice also that the board is marked USB to orient the Pico with the USB connector to the left. Finally, notice the two rows of double female headers. You connect your Pico to the centermost set leaving headers open for use with jumper wires. Cool!

Like all Grove components, Seeed Studio provides a Wiki page devoted to documenting the component and providing example code. The Wiki page for this host adapter is found at https://wiki.seeedstudio.com/Grove_Shield_for_Pi_Pico_V1.0/.

Sound Sensor

The Grove Sound Sensor is an analog module that incorporates a microphone and a small amplifier. It can be used to detect sound in the area and even the intensity of the sound. We will use both features in this project. Figure 10-2 shows the Grove Sound Sensor.
Figure 10-2

Grove Sound Sensor (courtesy of seeedstudio.com)

Grove RGB LED

The lamp used in this project is a bright red, green, and blue (RGB) LED that can be used to produce a vast array of colors by specifying a value of 0–255 for each color. The higher the value, the brighter (intensity) that color is shown. By mixing the intensity, we can see a wide range of colors.

For example, values of (255, 0, 0) for red or (127, 0, 127) for purple. To see what this might look like, an RGB chooser (www.w3schools.com/colors/colors_rgb.asp) can help you visualize the color. Navigate there now and try it out yourself.

The Grove Chainable RGB LED module allows you to produce any color you want. Figure 10-3 shows the Grove Chainable RGB LED.
Figure 10-3

Grove Chainable RGB LED (courtesy of seeedstudio.com)

So, what does the chainable in the name mean? It means if you want to use more than one RGB LED, you can “chain” the modules together. In fact, on the bottom of the module you will see two Grove connectors, one marked “IN” and another “OUT.” Figure 10-4 shows what the connectors look like. Notice the labels for each.
Figure 10-4

Grove Chainable RGB LED connectors on the bottom (courtesy of seeedstudio.com)

To chain multiple modules together, simply connect the first Grove cable from your host adapter to the “IN” connector, then another Grove cable to the “OUT,” and then the “IN” to the next module, and so on. You can connect up to 1024 RGBs together.

Grove Kits

Seeed Studio also sells kits for some platforms that include a host adapter and several modules. An excellent alternative kit for this project that has many of the sensors you will need is the Grove Starter Kit for Raspberry Pi Pico (www.seeedstudio.com/Grove-Starter-Kit-for-Raspberry-Pi-Pico-p-4851.html). Figure 10-5 shows the kit.
Figure 10-5

Grove Starter Kit for Raspberry Pi Pico (courtesy of seeedstudio.com)

Notice we see the Pico host adapter and the source sensor along with an LCD, environment sensors, LEDs, and much more. If you decide to purchase the kit, you can visit the Wiki at www.seeedstudio.com/Grove-Starter-Kit-for-Raspberry-Pi-Pico-p-4851.html to learn more about the components and see sample projects.

Now, let’s see how to connect the components together.

Set Up the Hardware

Connecting the hardware for a Grove project is really easy. Since the cables are keyed, you don’t have to worry about incorrect connections. Rather, we have to consider which Grove connectors we need to use. For this project, we need only two connectors, one for the I2C interface on the Chainable RGB module and an analog connector for the sound sensor. We will use the I2C0 connector for the RGB module and the A0 connector for the sound sensor as shown in Figure 10-6.
Figure 10-6

Connections for the sound detector project

Caution

Be sure to plug the Grove cable into the RGB module on the side that is indicated as “IN.” Plugging it into the “OUT” port may prevent the LED from illuminating. Recall, we use the “OUT” port to chain the RGB modules together by connecting the “OUT” of one module to the “IN” of the next in the chain.

Once again, always make sure to double-check your connections before powering the board on. Now, let’s talk about the code we need to write. Don’t power on your board just yet – there is a fair amount of discussion needed before we’re ready to test the project.

Write the Code

Now it’s time to write the code for our project. The code is a bit less complicated than the previous project but has its own interesting twists. Specifically, we will need a function to convert the value read from the sound sensor into a tuple of three values in the range 0–255 for the RGB values. Before we look at the code, let’s look at the library we will need to communicate with the Chainable RGB module that uses an I2C interface.

Libraries Needed

We need only one library for the Chainable RGB module since the sound sensor is an analog device that we can read with functions from the MicroPython machine library. If you visit the Wiki for the Chainable RGB module (https://wiki.seeedstudio.com/Grove-Chainable_RGB_LED/), you will discover a section that demonstrates how to use the module with Python on the Raspberry Pi. However, this example is for Python, not MicroPython. In fact, most of the Wiki pages for the Grove modules have only Python examples. So, what do we do? We turn to Google for help!

Tip

When researching Grove modules to use with the Pico, be sure to Google for a MicroPython driver. Most Python drivers for Grove modules require additional libraries or libraries that only work in Python.

A quick Google search for “chainable rgb grove micropython” will return a number of entries. Among them are example libraries for the p9813 chip, which is the controller chip used on the Chainable RGB module. The library found to work best for the Pico is the micropython-p9813 library from Mike Causer (https://github.com/mcauser/micropython-p9813).

We need only download the library to our PC and then upload it to our Pico. The best way to do that is to use the command git clone https://github.com/mcauser/micropython-p9813 to make a copy (clone) of the repository as follows. This copies all of the files including examples and documentation to your PC:
$ git clone https://github.com/mcauser/micropython-p9813
Cloning into 'micropython-p9813'...
remote: Enumerating objects: 36, done.
remote: Total 36 (delta 0), reused 0 (delta 0), pack-reused 36
Unpacking objects: 100% (36/36), done.

Once you clone the repository, you can locate the p9813.py file in the <root of clone>/micropython-p9813 folder. You can then download that to your Pico.

Now, let’s take a look at the code for the project.

Note

Since writing the code for a Grove project does not require any special programming, we will skip the line-by-line explanation and instead talk about the high-level parts of the code.

Code Layout

We will follow a similar code layout as we did in the previous project. We will create a main() function that will run when the Pico boots and helper functions for the more complicated parts. Thus, we will place all of our setup and the main execution loop in the main() function. Finally, we will add a conditional at the bottom of the script to call the main() function if the script is executed (loaded by MicroPython for execution).

Since we are familiar with how we write the code for our projects, let’s look at the code in overview starting from the top of the script file. We will name the file main.py.

Imports

Recall, we place the imports at the top of the file. The imports for the project will require the ADC and Pin classes from the machine library and the sleep library. We also need to import the p9813 library from the project4 folder. The following shows the imports for the project:
from machine import ADC, Pin
from time import sleep
from project4.p9813 import P9813

Functions

There are three helper functions needed for this project. We need a function to read a value from the sensor. We also need a function to translate the value read from the sound sensor to a tuple in the range of 0–255, 0–255, and 0–255 for the RGB values. We will break this operation into two parts to help with code comprehension.1

Listing 10-1 shows the code for the get_value() function. Here, we pass in an instance of the ADC class and use that to read ten values waiting for 100ms before each read. Like we have with the last analog sensor, we will read a series of values from the sensor and return an average to help reduce sporadic values from reading the sensor.
# Read the sensor 10 times and average the values read
def get_value(adc):
    total = 0
    for i in range (0,10):
        # Wait for sensor to settle
        sleep(0.1)
        # Read the value
        value = adc.read_u16()
        total += value
    return int(total/10)
Listing 10-1

The get_value() Function

Translating the value read from the sensor to a tuple is a bit more complicated than you may expect. There are two steps. First, we need to map the values to an integer. One mechanism2 to do this is to think of the tuple as three hexadecimal values in the range 0–255, which in hexadecimal is 0x00–0xFF. Now, if you arrange them consecutively such as 0xFFFFFF, you get a value of 16,777,215 or a range of 0–16,777,215. The second step is to convert to the RGB tuple; we shift the value 8 bits at a time to get each range.

We name the first step translate() and call that from within the second function named num_to_rbg(), which will call from the main() function.
# Translate from range 1-65353 to 1-16,777,215
def translate(x, in_min, in_max, out_min, out_max):
    return int((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
Listing 10-2

The translate() Function

Listing 10-3 shows the num_to_rgb() function. Take a moment to read the lines of code that do the bitwise shifts to ensure you understand how it works. In short, we first shift 16 bits to capture the leftmost value for red, then 8 bits and mask the extra bits (with the logical and operation) for green, and finally mask all except the rightmost value to capture the value for blue.
# Map range 0-0xFFFFFF to (R,G,B) tuple
def num_to_rgb(sensor_value):
    mapped_value = translate(sensor_value, LOW_THRESHOLD, 0xFFFF,
                             1, 0xFFFFFF)
    r = mapped_value >> 16
    g = (mapped_value >> 8) & 0x00FF
    b = (mapped_value & 0x0000FF)
    return (r,g,b)
Listing 10-3

The num_to_rgb() Function

Now let’s look at the main function.

Main Function

Next is the main() function. Here, we will initialize the variables we will need as well as implement the loop to keep the script running until cancelled or until the Pico shut down. Let’s begin with the setup code and then look at the main loop execution.

Setup

The setup code for this project will need to initialize the ADC class instance and create variables of the Pin class for use with the I2C interface for the Chainable RGB module. We also turn off the RGB LED by assigning the values of zero for each color in the tuple. There are two steps to setting the color for the RGB. First, we set the values for red, green, and blue and then call the write() function to tell the library to turn the LED on with those values. Listing 10-4 shows the code for setup and initialization.
# Setup the sound sensor
sound = ADC(Pin(26))
# Setup the RGB module
scl = Pin(7, Pin.OUT)
sda = Pin(6, Pin.OUT)
rgb_chain = P9813(scl, sda, 1)
rgb_chain[0] = (0, 0, 0) # turn RGB off
rgb_chain.write()
sleep(1)
Listing 10-4

Setup and Initialization

One thing to notice is that the P9813 class returns an array, not a single class instance. Thus, when we want to assign values to the RGB for the color, we must use the array index [0].

Execution Loop

Next, we examine the loop code. Here, we will greet the user and then get the value from the sound sensor. If the value is greater than the lowest threshold we established, we then convert the “sound” read to color and set the RGB to the new tuple (red, green, and blue). If the value from the sound sensor is lower or equal to the threshold, we turn the LED off. Listing 10-5 shows the code for the execution loop.
print("Welcome to the sound to light detector!")
while True:
    value = get_value(sound)
    if value > LOW_THRESHOLD:
        rgb = num_to_rgb(value)
        print("Value read: {0:05} Color: {1}".format(value, rgb))
        rgb_chain[0] = rgb
        rgb_chain.write()
        sleep(1)
        continue
    rgb_chain[0] = (0, 0, 0)
    sleep(0.25)
    rgb_chain.write()
Listing 10-5

Execution Loop

Now we’re all set to evaluate the code. We will write the code to execute automatically when we power on the Pico. Recall, we do this by naming the code main.py and placing the libraries we want to use in a folder named project4. Listing 10-6 shows the complete code for the project.
#
# Beginning MicroPython
#
# Chapter 10 – Sound to Light Detector
#
# This example implements a sound detector that turns on a RGB
# LED based on the value from the sound sensor. We use a Grove
# Sound Sensor and a Grove Chainable RGB LED.
#
# Dr. Charles Bell
#
# Import libraries
from machine import ADC, Pin
from time import sleep
from project4.p9813 import P9813
# Constants
LOW_THRESHOLD = 10000   # Threshold of the smallest sound value - tune to your environs
# Read the sensor 10 times and average the values read
def get_value(adc):
    total = 0
    for i in range (0,10):
        # Wait for sensor to settle
        sleep(0.1)
        # Read the value
        value = adc.read_u16()
        total += value
    return int(total/10)
# Translate from range 1-65353 to 1-16,777,215
def translate(x, in_min, in_max, out_min, out_max):
    return int((x - in_min) * (out_max - out_min) / (
                in_max - in_min) + out_min)
# Map range 0-0xFFFFFF to (R,G,B) tuple
def num_to_rgb(sensor_value):
    mapped_value = translate(sensor_value, LOW_THRESHOLD, 0xFFFF,
 1, 0xFFFFFF)
    r = mapped_value >> 16
    g = (mapped_value >> 8) & 0x00FF
    b = (mapped_value & 0x0000FF)
    return (r,g,b)
def main():
    # Setup the sound sensor
    sound = ADC(Pin(26))
    # Setup the RGB module
    scl = Pin(7, Pin.OUT)
    sda = Pin(6, Pin.OUT)
    rgb_chain = P9813(scl, sda, 1)
    rgb_chain[0] = (0, 0, 0) # turn RGB off
    rgb_chain.write()
    sleep(1)
    print("Welcome to the sound to light detector!")
    while True:
        value = get_value(sound)
        if value > LOW_THRESHOLD:
            rgb = num_to_rgb(value)
            print("Value read: {0:05} Color: {1}".format(value, rgb))
            rgb_chain[0] = rgb
            rgb_chain.write()
            sleep(1)
            continue
        rgb_chain[0] = (0, 0, 0)
        sleep(0.25)
        rgb_chain.write()
if __name__ == '__main__':
    try:
        main()
    except (KeyboardInterrupt, SystemExit) as err:
        print(" bye!")
        sys.exit(0)
Listing 10-6

Sound Detector Code

OK, now we’re ready to execute the project.

Execute

Now we can copy all of our code to our Pico. If you haven’t already done so, you should create a folder named project4 on your Pico and then upload the P9813.py file to the project4 folder. Next, upload the main.py file to the root folder of your Pico.

Recall, there are two ways to test or execute the code. We could use Thonny to connect to the Pico and simply run the main.py script. Or we can reboot the Pico by unplugging it and plugging it back in to the USB port on your PC.

The difference is if you run the main.py file manually, you will see the debug statements show in the output at the bottom of Thonny. Running the script automatically may not show those statements if you do not use Thonny or a similar application to connect to the Pico.

Once the program starts, you can then make some noise! Simply clap your hands or snap your fingers, or if you are careful, tap the sensor with a pen or pencil. Depending on the ambient noise in the room, you may need to adjust the minimal sound threshold so that the RGB illuminates only for louder noises. When values greater than the threshold are detected, the program writes debug statements to the REPL console. The following shows an example of the output you will see:
Welcome to the sound to light detector!
Value read: 15371 Color: (24, 194, 60)
Value read: 14249 Color: (19, 150, 46)
Value read: 13089 Color: (14, 61, 72)
Value read: 10904 Color: (4, 42, 204)
Value read: 13457 Color: (15, 239, 142)
Value read: 14291 Color: (19, 199, 191)
Value read: 12906 Color: (13, 101, 84)
Value read: 11816 Color: (8, 95, 9)
...

If you do not see the RGB illuminate and change colors when noises are varied in loudness, or the REPL console does not show any data, be sure to notice the sound values read and adjust the threshold up or down as needed.

Once everything is working, you can disconnect your Pico and connect it to a 5V power supply to run the project on boot and watch the colors change with sound. Cool!

Taking It Further

This project, like the last one, shows excellent prospects for reusing the techniques in other projects. Sound sensors can be applied to many problems, and if you want to consider taking time to explore some embellishments, here are a few you may want to consider:
  • Experiment with different sounds in the room like opening and closing doors or windows and adjust the code to detect those sounds. Perhaps even assign special colors to those sound levels.

  • Add more RGB LEDs to your chain and program the code to send the color codes to all of the RGBs in the chain.

  • Adapt the code to use a set of colors for differing loudness (sounds). This could be helpful or interesting if you want to display color changes to sound levels such as applause from an audience.

Of course, if you want to press on to the next project, you’re welcome to do so, but take some time to explore these potential embellishments – it will be a good practice.

Summary

As you can see, using Grove modules is much easier than trying to connect a set of jumper wires to breakout boards or building a circuit on a breadboard. In this regard, the Grove system is a grand success. Best of all, it allows you to quickly assemble the hardware of your project so you can concentrate on the code.

In this chapter, we took a look at a sample Grove project that uses an I2C device and an analog sensor. We saw how to connect the modules to our host board as well as how to adapt code to work with the Pico. This small project has also shown us how easy it is to use Grove modules, and now that we’ve had some practice with a simple example, we are ready to jump into a more complex project.

In the next chapter, we will see another project that demonstrates how to use more Grove modules to create a classic electronic game called Simon Says. It’s time to have some fun!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.239.11.178