Experimenting with BoneScript - Programming with BoneScript - BeagleBone For Dummies (2015)

BeagleBone For Dummies (2015)

Part III

Programming with BoneScript

Chapter 8

Experimenting with BoneScript

In This Chapter

Discovering how to interact with your circuits

Adjusting the brightness of an RGB LED

Moving a servomotor with a potentiometer

Detecting whether someone entered your room

The more you know about BoneScript, the easier it gets to see the endless possibilities for playing around with this programming language. This chapter shows you how to turn on a light by pressing a pushbutton, how to control a motor’s rotation through the use of a potentiometer, and how to automatically turn on a light that warns you that someone has entered a room.

At first glance, all the projects in this chapter may seem straightforward and simple, just as most of the electronic devices you engage with are. For example, on a daily basis you encounter things that have buttons, knobs, and sensors that control some output. This chapter walks you through the most basic, yet most important, functions that BoneScript offers. Later, you’ll be able to incorporate the building blocks presented in this chapter into your own unique projects.

The instructions in this chapter assume that you can connect your BeagleBone to your computer (see Chapter 3), you already know how to open the Cloud9 integrated development environment (IDE), and you can create a new file inside your Projects folder (see Chapter 7). There are also some BoneScript functions used in this chapter that are covered in Chapter 7, and you should read it if you need an explanation on them.

Reading an Input

In the BoneScript projects in the previous chapter of this book, you work only with outputs, defining a pin as an output and then running a script that set that pin to either HIGH or LOW. The result is a blinking light-emitting diode (LED). You make something happen in the world, which means that your pin works as an output.

At times, though, you want to interact with your circuit. Often, it’s desirable for the BeagleBone to read from something that happened in the world, process that information, and then make something happen.

When we say that the BeagleBone is reading from something, it means that it is using an input to receive information such as sensor data or a button press.

The first step of that process is reading the state of a digital pin that’s set as an input. In this example, you use a pushbutton to control whether a pin is HIGH (when the pushbutton is pressed) or LOW (when it isn’t pressed) and then make the BeagleBone read that information and output the state of the button to a computer screen.

You need the following supplies to get started:

· A pushbutton

· A 10K Ω resistor

· A breadboard

· Jumper wires

Wiring a pushbutton

Figure 8-1 shows a circuit with a pushbutton and a resistor. Most pushbuttons are designed to be inserted right into the middle gap of the breadboard, just like the one in the schematic. This circuit is pretty easy to assemble.

If you have a pushbutton that’s slightly different from the one used in this example, you must test its continuity with a multimeter. (You can read about using a multimeter at www.dummies.com/extras/beaglebone. Test whether the pushbutton leads that you’re using are connected when the pushbutton is pressed and disconnected when the pushbutton is released.

Figure 8-1: Pushbutton connected to the BeagleBone’s input pin P8_11.

Follow these steps to wire your circuit:

1. Connect the BeagleBone’s ground (GND) — pin P9_1 or P9_2 — to the negative track of the breadboard.

2. Use a jumper to connect P8_11 to a vertical row on your breadboard.

This pin is the one you’ll be using as an input.

3. Connect the BeagleBone’s 3.3V source — pin P9_3 or P9_4 — to the breadboard’s positive track.

4. Connect the pushbutton.

Place it in the center of the board to ensure that each pair of legs is electrically separated.

5. Using jumpers, connect one of the pushbutton’s legs to the positive rail of the breadboard and the other leg to the jumper that comes from P8_11.

6. Connect a pull-down resistor between the other leg of the pushbutton and the negative track of the breadboard.

Writing the code to read the state of a pushbutton

We encourage you to keep all your files inside the Projects folder so that everything is well organized. To get started with the code, create a new file called readButton.js in your Projects folder.

Write the following in your readButton.js script:

var b = require('bonescript');

var button = 'P8_11';

b.pinMode(button, b.INPUT);
b.digitalRead(button, printStatus);

function printStatus(x) {
console.log('Button state = ' + x.value);
}

The first two lines of code start like all the examples presented in Chapter 7. You’re loading the BoneScript module into your script and creating a variable called button that refers to P8_11.

On the third line of code, you set the button as an INPUT with the function pinMode(). Next comes a function that enables you to read the state of your digital pins:

digitalRead(<GPIO>, [callback])

This function takes two parameters:

· GPIO: This pin is the one for which you want to know the state. In this case, the pin is P8_11, which you define in the button variable.

· callback: This parameter, called upon completion, returns the button value — HIGH or LOW — stored in the x.value variable.

Callbacks are functions that you use frequently when working with JavaScript. In this example, as soon as the digitalRead() function completes its process, it executes the callback function printStatus(). The printStatus(x) function takes a single parameter, which is the returned value from the digitalRead() function, referred by the letter x in parentheses. In the case of digitalRead(), the value returned is either HIGH or LOW.

The last section of code prints on your console the state of your button, which is stored in the x.value variable:

function printStatus(x) {
console.log('Button state = ' + x.value);
}

The console.log('<your message>') function is the easiest way to print something to the console. Note that you can print a message you write inside it, such as 'Button state = ', as well as the current value of a variable, such as x.value. You put the message and the variable together with a plus (+).

Apostrophes aren’t used to print variable values. Typing console.log('Button state = ' + 'x.value'), with the x.value inside apostrophes, prints the line Button state = x.value rather than the actual value of the variable.

The console.log('<your message>') function gives the user some feedback on what’s happening while the code is running. It’s one of the best tools for debugging your code as you experience later in Chapter 11.

Running the script to read the state of a pushbutton

When you have all the code written, press Ctrl+S or Cmd+S to save it and then click the Run button or press F5 to execute your script.

Take a look at your console output. If you weren’t pressing the button, the message Button state = 0 should have been printed.

Conversely, if you run the script again while pressing the button (see Figure 8-2), you see on your screen the line Button state = 1.

The terms HIGH and LOW refer to 1 and 0, respectively.

Figure 8-2: Pushbutton wired to a BeagleBone Black.

Controlling an LED with a Pushbutton

In this project, you find out how to control the state of an output depending on the state of an input — by turning an LED on and off when you press or release a pushbutton. This project also introduces the concept of interrupts.

Interrupts are clever ways to wait for something to change. With the attachInterrupt() function, you can create a function that waits for an input pin to change its state by going from LOW to HIGH or the reverse. When that happens, attachInterrupt() automatically detects that change and executes a function. This function is useful because it solves some timing issues. The alternative would be to create a script that waits for a button to change, which would be really difficult.

You need the following components:

· A pushbutton

· A 10K Ω resistor

· An LED

· A 220 Ω or 470 Ω resistor

· A breadboard

· Jumper wires

Wiring an LED and a pushbutton

This example is very straightforward to prepare if you’ve worked through examples in the previous chapter and in the “Reading an Input” section presented earlier in this chapter because you know how to wire an LED and a pushbutton. We won’t go into much detail on how to wire this circuit; simply follow the circuit diagram shown in Figure 8-3. You can also follow the steps in the aforementioned sections.

Figure 8-3: An LED and a pushbutton wired to a BeagleBone Black.

Writing the code

When everything is wired up, power up your BeagleBone, and start coding the next script. Create a new file called buttonLED.js, and type the following code in it:

var b = require('bonescript');

var led = "P9_14";
var button = "P8_11";

b.pinMode(led, b.OUTPUT);
b.pinMode(button, b.INPUT);

var state = b.LOW;
b.digitalWrite(led, state);

b.attachInterrupt(button, true, b.CHANGE, toggle);

function toggle(x) {
if (x.value == b.HIGH) {
console.log("The button is HIGH");
b.digitalWrite(led, b.HIGH);
}
else {
console.log("The button is LOW");
b.digitalWrite(led, b.LOW);
}
}

The first portion of code is identical to what you’ve used in the previous examples in this chapter. This line is where the code gets interesting:

b.attachInterrupt(button, true, b.CHANGE, toggle);

You’re going to use the attachInterrupt() function to create an interrupt that triggers the toggle() function when someone presses or releases the button:

attachInterrupt(<GPIO>, <handler>, <mode>, [callback])

This function takes up to four parameters:

· GPIO: This pin is the one for which you want to know the state. In this case, the pin is P8_11, defined in the button variable.

· handler: You can simply set this parameter as true so that it always calls the callback if an interrupt occurs. Alternatively, you can set it as a string that’s evaluated and, if true, it executes the callback. In this example, you don’t want to evaluate handler’s value; you want it to always be true because you want to execute the toggle() function every time an interrupt occurs. In fact, this parameter is true in most situations.

· mode: The three types of modes are RISING, FALLING, and CHANGE. In this example, you use CHANGE mode, because you want to toggle the LED when the button is pressed and when the button is released. You’d use RISING if you only wanted to check for the button’s being pressed, whereas FALLING refers to the button’s being released.

· callback: This parameter is called upon completion — that is, when a change occurs on the input pin — if the handler parameter is true.

All the magic happens in the toggle() function:

function toggle(x) {
if (x.value == b.HIGH) {
console.log("The button is HIGH");
b.digitalWrite(led, b.HIGH);
}
else {
console.log("The button is LOW");
b.digitalWrite(led, b.LOW);
}
}

When an interrupt occurs, it saves the current state in the x.value variable. So if (x.value == b.HIGH) — that is, if the button is pressed — you want to toggle the LED to HIGH. When another interrupt occurs — when you release the pushbutton — your LED toggles to LOW.

Running the script

Save your project and then click Run or press F5.

Your LED should stay on for as long as you hold down the pushbutton (see Figure 8-4), and it should turn off the moment you release the pushbutton.

If the LED is too dim, use a 220 Ω resistor rather than a 470 Ω resistor.

Figure 8-4: The LED stays on as long as the pushbutton is pressed.

Knowing how to control output pins depending on the state of input pins is a very important asset and one of the most prominent building blocks of advanced electronics projects. Just as your pushbutton controls an LED, you could have an infrared sensor controlling an alarm that goes off whenever someone attempts to raid your fridge. In terms of wiring and coding, the situation is pretty much the same. You have a digital input — HIGH for “fridge door open” and LOW for “fridge door closed” — and a digital output in the form of the alarm.

Adjusting the Brightness with an RGB LED

Everyone loves LEDs. How much cooler could things get with an LED that has three colors and can combine them to create some color effects?

These little pieces of awesomeness are known as RGB (red-green-blue) LEDs. In this project, you find out how to control them by using pulse-width modulation (PWM) with BoneScript and by using interrupts. PWM is covered in more detail in Chapter 6.

The circuit you build in this section consists of an RGB LED whose color and brightness are controlled by pressing a pushbutton.

You need the following components:

· An RGB common cathode LED

· 4x 220 Ω or 470 Ω resistors

· A breadboard

· Jumper wires

Wiring the RGB LED

There are two types of RGB LEDs:

· Common cathode: The longest lead is connected to the ground pin. Then you connect the other leads to output pins. Except for the longest lead, all leads are associated with different colors; having a lead connected to a pin in the HIGH state lights its color. For leads connected to pins in the LOW state, those colors are off.

· Common anode: The longest lead is connected to the power pin. Then you connect the other leads to output pins, and things work in reverse: LOW values turn a color of the LED on, whereas HIGH turns that color off. If you use a common anode LED, you have to alter your code and wiring according to this reversal of what HIGH and LOW on each lead does.

In the preceding section, we say that you require an RGB common cathode LED. You can use a common anode LED, but keep in mind some things will be slightly different than what’s described in this section.

You control the color of an RGB LED by deciding which leads are HIGH and which are LOW.

Follow these steps to wire everything together:

1. Wire a pushbutton and a pull-down resistor to your breadboard.

Refer to Figure 8-1 earlier in this chapter to verify the wiring.

2. Use a jumper to connect P9_14 to a vertical row on your breadboard.

This pin will control the red color of your RGB LED.

3. Use a jumper to connect P9_16 to a vertical row on your breadboard.

This pin will control the green color of your RGB LED.

4. Use a jumper to connect P8_13 to a vertical row on your breadboard.

This pin will control the blue color of your RGB LED.

Use color coding to keep yourself organized. In this example, using jumper wires that are red for P9_14, green for P9_16, and blue for P8_13 probably would be a good idea.

5. To each of these jumpers, wire a 200 Ω or 470 Ω resistor.

6. Wire the longer lead of your RGB LED to ground (GND) on the breadboard rail.

7. Check the schematic in Figure 8-5 to make sure that you have the wiring right.

Figure 8-5: RGB wired to a BeagleBone Black

You can use eight pins use as PWM pins. Refer to Chapter 6 for more information.

Writing the code

Create a new file called RGB.js, and type the following script:

var b = require('bonescript');
var RGB = [ "P9_14", "P9_16", "P8_13"];
var button = "P8_11";
var RGB_lead=0;
var brightness=0;

for ( var i = 0; i < 3; i++ ) {
b.pinMode(RGB[i], b.OUTPUT);
}

b.pinMode(button, b.INPUT);

b.attachInterrupt(button, true, b.RISING, bright);

function bright() {
if(RGB_lead < 3) { // do this for each color defined by each of the RGB LED's leads.
if(brightness < 1) { //brightness is increased until maximum
brightness=brightness+0.25; // brightness is increased 25% at a time.
b.analogWrite(RGB[RGB_lead], brightness, 2000, console.log('Brighter')); // write the PWM values into the current RGB_lead
}
else { //when one of the colors reaches the maximum brightness
brightness=0; // turn that color off
b.analogWrite(RGB[RGB_lead], brightness, 2000, console.log('RGB lead off'));
RGB_lead++;
}
}
else { // when the program has gone through the three RGB LED's leads, start over
RGB_lead=0;
}
}

In the first lines of code, you load your BoneScript module, and you create some variables and an array:

var b = require('bonescript');

var RGB = [ "P9_14", "P9_16", "P8_13"];
var button = "P8_11";
var RGB_lead=0;
var brightness=0;

Then you configure all pins as outputs with a for loop. You also set a button as an input:

for ( var i = 0; i < 3; i++ ) {
b.pinMode(RGB[i], b.OUTPUT);
}
b.pinMode(button, b.INPUT);

Next, you create an interrupt that executes every time you press the pushbutton. This interrupt is activated with a RISING edge and calls the bright() function:

b.attachInterrupt(button, true, b.RISING, bright);

The bright() function is the main character of this code. Spanning the entire function is an if...else statement that checks whether the code went through all the RGB LED leads:

if(RGB_lead < 3) {
(...)
}
else {
(...)
}

Inside that main if, the program has another if statement that checks whether the maximum brightness of 1 has been reached:

if(brightness < 1) {
(...)
}
else {
(...)
}

Before we get into more detail, you need to be aware of the function called analogWrite():

analogWrite(<GPIO>, <value>, [freq], [callback])

The analogWrite() function takes four parameters:

· GPIO: This pin is the one to apply voltage. In this case, the pins are P9_14, P9_16, and P8_14, which you define in the RGB array.

· value: The duty cycle of the PWM can be any value between 0 and 1. This value defines the fraction of the duty cycle with regard to the PWM frequency, which in this case defines the LED’s brightness.

· freq: The default frequency of the PWM is 2000 Hz.

· callback: Call this function upon completion.

See Chapter 6 for the theory behind PWM.

analogWrite() gives a GPIO some voltage between 0V and 3.3V (LOW and HIGH, respectively). In this circuit, it’s the function that actually increases the brightness defined by each of the RGB LED’s leads:

brightness=brightness+0.25;
b.analogWrite(RGB[RGB_lead], brightness, 2000, console.log('Brighter'));

The same function is used to turn an RGB LED lead off:

brightness=0;
b.analogWrite(RGB[RGB_lead], brightness, 2000, console.log('RGB lead off'));

Running the script

Don’t forget to save your code before you run it by pressing Ctrl+S or Cmd+S. Then click the green Run button or press F5, and try out your project. When you press the pushbutton, you see the LED’s brightness increase, and if you press it a couple of times, the LED goes through all the RGB LEDs colors.

If the LED is too dim, use 220 Ω resistors rather than 470 Ω resistors.

Note: If you press the pushbutton too quickly, brightness may increase by two levels.

You can use the PWM concepts in this project to dim the light on your desk or control a servomotor.

Sweeping a Servo with a Potentiometer

This project shows you how to read analog inputs with yet another extremely useful BoneScript function: analogRead(). As its name suggests, this function allows you to use input pins to read analog values. You also have the opportunity to practice PWM. By putting these two concepts together, you see how you can control the rotation of a servo with a potentiometer.

You need the following components:

· A servomotor that operates at 3.3V

· A 1K Ω resistor

· A 10K Ω potentiometer

· A breadboard

· Jumper wires

The next section introduces some important matters regarding analog inputs.

Analog inputs

As mentioned in Chapter 6, PWM produces a “fake” analog signal. In reality, this signal is just a digital signal that alternates between the values HIGH and LOW very quickly, making systems (such as the human eye when it comes to LEDs) perceive that things are working somewhere in the middle. Thus, the BeagleBone doesn’t require DACs (digital-to-analog converters) because PWM does the trick just fine.

The real world is an analog world, not a digital one. Therefore, when it comes to reading information from the world, you find that inputs can have a varied range of values, such as temperature, humidity, and light levels or even the resistance of a potentiometer. Due to this fact, analog inputs are necessary, which ultimately led to the BeagleBone’s featuring internal ADCs (analog-to-digital converters).

The BeagleBone has seven ADCs and their mission is simple: Read analog voltages between 0V and 1.8V and put them to scale (that is, convert them to values 0 to 1).

Wiring the test circuit

You can verify that an ADC is doing its job by using a very simple circuit and code. All you have to do is wire up a potentiometer (see Figure 8-6) as follows:

· One of the outer leads goes into GND.

· The middle lead goes into the P9_40 pin of the BeagleBone. You’ll be using this pin for the ADC.

· The other outer lead goes into P_32. This pin is a 1.8 V source, which is the maximum value that the ADCs on the BeagleBone can handle.

Figure 8-6: Potentiometer wired to a BeagleBone Black.

Writing the test code

Create a new file called potentiometer.js, and type the following script:

var b = require('bonescript');

var pot = 'P9_40';

b.analogRead(pot, printADC);

function printADC(x) {
console.log(x.value);
b.analogRead(pot, printADC);
}

This code is quite straightforward. The first analogRead() function reads the value of pin P9_40 and then uses a callback to the printADC() function, which prints the value of the analog input pin and executes analogRead() again. The analogRead() function takes two parameters:

analogRead(<GPIO>, [callback])

The two parameters for the analogRead() function are the following:

· GPIO: This pin is the one you want to read the analog input value. In this case, the pin is P8_40, which you define in the pot variable.

· callback: Call this parameter upon completion. It returns a value stored in the x.value object of the analog input value.

Running the test script

When you run this script, you should see your console output filling with numbers, all of them below 1. As you rotate the knob on the potentiometer, these numbers vary between 0 and 1, depending on the voltage level at the input pin.

The values may never reach 0 or 1, but may be at some values very, very close to those.

Wiring everything together

To build your circuit and connect it to your BeagleBone, you use an analog input pin (see Figure 8-7). Follow these steps:

1. Connect the BeagleBone’s ground (GND) — pin P9_1 or P9_2 — to the negative track of the breadboard.

2. Connect the BeagleBone’s 3.3V source — pin P9_3 or P9_4 — to the breadboard’s positive track.

3. Connect the servo brown or black wire to the GND breadboard rail.

4. Connect the servo red wire to the 3.3V breadboard rail.

5. Place a 1K Ω resistor in your breadboard and then connect the servo orange or yellow wire to the resistor.

6. Connect the other lead of the 1K Ω resistor to the P9_14 pin in your BeagleBone.

7. Connect the left lead of your potentiometer to the GND breadboard rail.

8. Connect the middle lead of your potentiometer to the P9_40 pin in your BeagleBone.

9. Connect the right lead of your potentiometer to the P9_32 pin in your BeagleBone.

Your ADC pins can handle a maximum 1.8V, which is why you connect the source lead of your potentiometer to pin P9_32.

Figure 8-7: Potentiometer and servo wired to a BeagleBone Black.

Writing the code to sweep a servo with a potentiometer

Create a new file called sweepingServo.js in your Projects folder, and type the following:

var b = require('bonescript');
var servo = 'P9_14';
var pot = 'P9_40';
var duty_min = 0.03;

b.pinMode(servo, b.OUTPUT);

b.analogRead(pot, updatePosition);

function updatePosition(x) {
var position = x.value
var duty_cycle = (position*0.115) + duty_min;
b.analogWrite(servo, duty_cycle, 60, nextUpdate);
}

function nextUpdate() {
b.analogRead(pot, updatePosition);
}

In the first five lines of code, you load the BoneScript module, create a few handy variables, and set the servo as an OUTPUT.

Next, you use the analogRead() function. This function calls the updatePosition() function, sending to it the current potentiometer position. This position is stored in the x.value object. As demonstrated in the following code snippet:

b.analogRead(pot, updatePosition);

Then you create a new variable to store the current position and adjust the duty_cycle. The values 0.115 and duty_min should be used to relate the position of the potentiometer to the duty_cycle of the PWM, and there is no need for you to worry about those. Simply use the following code:

var position = x.value
var duty_cycle = (position*0.115) + duty_min;

Finally, you move the servo to this new position and execute the nextUpdate() function to check again whether the potentiometer has moved. The code stays in that loop, keeps checking for new movements, and changes the servo according to the potentiometer position:

b.analogWrite(servo, duty_cycle, 60, nextUpdate);

Running the script to sweep a servo with a potentiometer

After saving and running your project, you should be able to see your servo moving according to the way you move the potentiometer (see Figure 8-8).

Figure 8-8: Sweep your servo by rotating your potentiometer.

You could use a similar approach to control a robot servo with a remote control, which works the same way. Most robots are moved by servomotors, and remote controls have joysticks with potentiometers. If building a robot is something that piques your interest, you’re now a little bit closer to that goal!

Most servomotors can rotate only 180 degrees.

Detecting Movement with a Motion Sensor

Have you ever wondered why a light automatically turns on when you arrive in a building or when you enter a public restroom? In this example, you simulate that situation using a PIR (passive infrared) sensor and an LED.

You need the following components:

· A PIR motion sensor

· An LED

· A 220 Ω or 470 Ω resistor

· A breadboard

· Jumper wires

Wiring the motion sensor

You can start by wiring an LED and a 220 Ω or 470 Ω resistor to the same digital pins that you use throughout this chapter. (If the LED is too dim, use a 220 Ω resistor rather than a 470 Ω resistor.) Wire the PIR motion sensor (see Figure 8-9) as follows:

· VCC: Connect the sensor red wire to the breadboard rail and then connect the BeagleBone’s P9_7 or P9_8 pin to it.

This PIR motion sensor requires 5V to operate.

· OUT: Connect the output orange wire to the P8_19 pin.

· GND: Connect the sensor black wire to the GND breadboard rail.

Writing the code for motion detection

Create a new file called motionSensor.js in your Projects folder, and type the following script:

var b = require('bonescript');

var led = "P9_14";
var pir = "P8_19";

b.pinMode(led, b.OUTPUT);
b.pinMode(pir, b.INPUT);

b.digitalWrite(led, b.LOW);

setInterval (checkMotion, 2000);

function checkMotion() {
b.digitalRead(pir, activate);
function activate(x){
if (x.value == b.HIGH) {
b.digitalWrite(led, b.HIGH);
console.log("Motion detected");
}
else {
b.digitalWrite(led, b.LOW);
console.log("No motion detected");
}
}
}

Figure 8-9: PIR sensor and LED wired to a BeagleBone Black.

As always, you start by loading the BoneScript module and defining variables. Set the pin P9_14, which refers to the LED as an OUTPUT, and set the PIR motion sensor as an INPUT, which refers to the PIR motion sensor’s OUT pin.

Next, you use the digitalWrite() function to set the default LED state to LOW. Subsequently, you use the setInterval() function to execute the checkMotion() function every 2 seconds to look for any movement in front of your sensor.

The last step is creating a function called checkMotion(). That function starts by reading the digital value of the PIR motion sensor and then calls the activate() function, which works by checking whether the value read from the PIR INPUT pin is HIGH. If so, the function turns on the LED and prints a message to the console saying that motion has been detected. If the value read from the PIR INPUT pin is LOW, the function turns off the LED and tells the user that no motion was detected.

Running the script for motion detection

When you’re running this code, you receive feedback in two ways:

· If no motion is detected, the LED remains off, and No motion detected is printed on your console (see Figure 8-10).

Figure 8-10: The motion sensor circuit with no motion detected.

· If motion is detected, the LED lights up, and Motion detected is printed on your console (see Figure 8-11).

Figure 8-11: Motion detected!

Now you know how those automatic lights work and have even built one yourself. If you want the entire building to hear when someone raids your fridge or enters your room, all you have to do is replace the LED with a buzzer, which is wired exactly the same way.