Internet of Things#

You can easily connect BeagleBone Black to the Internet via a wire (Establishing an Ethernet-Based Internet Connection), wirelessly (Establishing a WiFi-Based Internet Connection), or through the USB to a host and then to the Internet (Sharing the Host’s Internet Connection over USB). Either way, it opens up a world of possibilities for the “Internet of Things” (IoT).

Now that you’re online, this chapter offers various things to do with your connection.

Accessing Your Host Computer’s Files on the Bone#

Problem#

You want to access a file on a Linux host computer that’s attached to the Bone.

Solution#

If you are running Linux on a host computer attached to BeagleBone Black, it’s not hard to mount the Bone’s files on the host or the host’s files on the Bone by using sshfs. Suppose that you want to access files on the host from the Bone. First, install sshfs:

bone$ sudo apt install sshfs

Now, mount the files to an empty directory (substitute your username on the host computer for username and the IP address of the host for 192.168.7.1):

bone$ mkdir host
bone$ sshfs username@$192.168.7.1:. host
bone$ cd host
bone$ ls

The ls command will now list the files in your home directory on your host computer. You can edit them as if they were local to the Bone. You can access all the files by substituting :/ for the :. following the IP address.

You can go the other way, too. Suppose that you are on your Linux host computer and want to access files on your Bone. Install sshfs:

host$ sudo apt install sshfs

and then access:

host$ mkdir /mnt/bone
host$ sshfs debian@$192.168.7.2:/ /mnt/bone
host$ cd /mnt/bone
host$ ls

Here, we are accessing the files on the Bone as debian. We’ve mounted the entire file system, starting with /, so you can access any file. Of course, with great power comes great responsibility, so be careful.

The sshfs command gives you easy access from one computer to another. When you are done, you can unmount the files by using the following commands:

host$ umount /mnt/bone
bone$ umount home

Serving Web Pages from the Bone#

Problem#

You want to use BeagleBone Black as a web server.

Solution#

BeagleBone Black already has the nginx web server running.

When you point your browser to 192.168.7.2, you are using the nginx web server. The web pages are served from /var/www/html/. Add the HTML in A sample web page (test.html) to a file called /var/www/html/test.html, and then point your browser to 192.168.7.2:/test.html.

Listing 55 A sample web page (test.html)#
 1<!DOCTYPE html>
 2<html>
 3<body>
 4
 5<h1>My First Heading</h1>
 6
 7<p>My first paragraph.</p>
 8
 9</body>
10</html>

test.html

You will see the web page shown in test.html as served by nginx.

test.html served by nginx

Fig. 629 test.html as served by nginx#

Interacting with the Bone via a Web Browser#

Problem#

BeagleBone Black is interacting with the physical world nicely and you want to display that information on a web browser.

Solution#

Flask is a Python web framework built with a small core and easy-to-extend philosophy. Serving Web Pages from the Bone shows how to use nginx, the web server that’s already running. This recipe shows how easy it is to build your own server. This is an adaptation of Python WebServer With Flask and Raspberry Pi.

First, install flask:

bone$ sudo apt update
bone$ sudo apt install python3-flask

All the code in is the Cookbook repo:

bone$ git clone https://git.beagleboard.org/beagleboard/beaglebone-cookbook-code
bone$ cd beaglebone-cookbook-code/06iot/flask

First Flask - hello, world#

Our first example is helloWorld.py

Listing 56 Python code for flask hello world (helloWorld.py)#
 1#!/usr/bin/env python
 2# From: https://towardsdatascience.com/python-webserver-with-flask-and-raspberry-pi-398423cc6f5d
 3
 4from flask import Flask
 5app = Flask(__name__)
 6@app.route('/')
 7def index():
 8    return 'hello, world'
 9if __name__ == '__main__':
10    app.run(debug=True, port=8080, host='0.0.0.0')

helloWorld.py

  1. The first line loads the Flask module into your Python script.

  2. The second line creates a Flask object called app.

  3. The third line is where the action is, it says to run the index() function when someone accesses the root URL (‘/’) of the server. In this case, send the text “hello, world” to the client’s web browser via return.

  4. The last line says to “listen” on port 8080, reporting any errors.

Now on your host computer, browse to 192.168.7.2:8080 flask an you should see.

Test page

Fig. 630 Test page served by our custom flask server#

Adding a template#

Let’s improve our “hello, world” application, by using an HTML template and a CSS file for styling our page. Note: these have been created for you in the “templates” sub-folder. So, we will create a file named index1.html, that has been saved in /templates.

Here’s what’s in templates/index1.html:

Listing 57 index1.html#
1<!DOCTYPE html>
2   <head>
3      <title>{{ title }}</title>
4   </head>
5   <body>
6      <h1>Hello, World!</h1>
7      <h2>The date and time on the server is: {{ time }}</h2>
8   </body>
9</html>

index1.html

Note: a style sheet (style.css) is also included. This will be populated later.

Observe that anything in double curly braces within the HTML template is interpreted as a variable that would be passed to it from the Python script via the render_template function. Now, let’s create a new Python script. We will name it app1.py:

Listing 58 app1.py#
 1#!/usr/bin/env python
 2# From: https://towardsdatascience.com/python-webserver-with-flask-and-raspberry-pi-398423cc6f5d
 3
 4'''
 5Code created by Matt Richardson 
 6for details, visit:  http://mattrichardson.com/Raspberry-Pi-Flask/inde...
 7'''
 8from flask import Flask, render_template
 9import datetime
10app = Flask(__name__)
11@app.route("/")
12def hello():
13   now = datetime.datetime.now()
14   timeString = now.strftime("%Y-%m-%d %H:%M")
15   templateData = {
16      'title' : 'HELLO!',
17      'time': timeString
18      }
19   return render_template('index1.html', **templateData)
20if __name__ == "__main__":
21   app.run(host='0.0.0.0', port=8080, debug=True)
22  

app1.py

Note that we create a formatted string (“timeString”) using the date and time from the “now” object, that has the current time stored on it.

Next important thing on the above code, is that we created a dictionary of variables (a set of keys, such as the title that is associated with values, such as HELLO!) to pass into the template. On “return”, we will return the index1.html template to the web browser using the variables in the templateData dictionary.

Execute the Python script:

bone$ .\app.py

Open any web browser and browse to 192.168.7.2:8080. You should see:

app1.py

Fig. 631 Test page served by app1.py#

Note that the page’s content changes dynamically any time that you refresh it with the actual variable data passed by Python script. In our case, “title” is a fixed value, but “time” changes every minute.

Displaying GPIO Status in a Web Browser - reading a button#

Problem#

You want a web page to display the status of a GPIO pin.

Solution#

This solution builds on the Flask-based web server solution in Interacting with the Bone via a Web Browser.

To make this recipe, you will need:

  • Breadboard and jumper wires.

  • Pushbutton switch.

Wire your pushbutton as shown in Diagram for wiring a pushbutton and magnetic reed switch input. Wire a button to P9_11 and have the web page display the value of the button.

Let’s use a new Python script named app2.py.

Listing 59 A simple Flask-based web server to read a GPIO (app2.py)#
 1#!/usr/bin/env python
 2# From: https://towardsdatascience.com/python-webserver-with-flask-and-raspberry-pi-398423cc6f5d
 3import os
 4from flask import Flask, render_template
 5app = Flask(__name__)
 6
 7pin = '30' #  P9_11 is gpio 30
 8GPIOPATH="/sys/class/gpio"
 9buttonSts = 0
10
11# Make sure pin is exported
12if (not os.path.exists(GPIOPATH+"/gpio"+pin)):
13    f = open(GPIOPATH+"/export", "w")
14    f.write(pin)
15    f.close()
16
17# Make it an input pin
18f = open(GPIOPATH+"/gpio"+pin+"/direction", "w")
19f.write("in")
20f.close()
21
22@app.route("/")
23def index():
24	# Read Button Status
25	f = open(GPIOPATH+"/gpio"+pin+"/value", "r")
26	buttonSts = f.read()[:-1]
27	f.close()
28
29	# buttonSts = GPIO.input(button)
30	templateData = {
31      'title' : 'GPIO input Status!',
32      'button'  : buttonSts,
33      }
34	return render_template('index2.html', **templateData)
35if __name__ == "__main__":
36   app.run(host='0.0.0.0', port=8080, debug=True)

app2.py

What we are doing is defining the button on P9_11 as input, reading its value and storing it in buttonSts. Inside the function index(), we will pass that value to our web page through “button” that is part of our variable dictionary: templateData.

Let’s also see the new index2.html to show the GPIO status:

Listing 60 A simple Flask-based web server to read a GPIO (index2.html)#
 1<!DOCTYPE html>
 2   <head>
 3      <title>{{ title }}</title>
 4      <link rel="stylesheet" href='../static/style.css'/>
 5   </head>
 6   <body>
 7	  <h1>{{ title }}</h1>
 8      <h2>Button pressed:  {{ button }}</h1>
 9   </body>
10</html>

index2.html

Now, run the following command:

bone$ ./app2.py

Point your browser to http://192.168.7.2:8080, and the page will look like Status of a GPIO pin on a web page.

GPIO status

Fig. 632 Status of a GPIO pin on a web page#

Currently, the 0 shows that the button isn’t pressed. Try refreshing the page while pushing the button, and you will see 1 displayed.

It’s not hard to assemble your own HTML with the GPIO data. It’s an easy extension to write a program to display the status of all the GPIO pins.

Controlling GPIOs#

Problem#

You want to control an LED attached to a GPIO pin.

Solution#

Now that we know how to “read” GPIO Status, let’s change them. What we will do will control the LED via the web page. We have an LED connected to P9_14. Controlling remotely we will change its status from LOW to HIGH and vice-versa.

Create a new Python script and name it app3.py.

Listing 61 A simple Flask-based web server to read a GPIO (app3.py)#
 1#!/usr/bin/env python
 2# From: https://towardsdatascience.com/python-webserver-with-flask-and-raspberry-pi-398423cc6f5d
 3# import Adafruit_BBIO.GPIO as GPIO
 4import os
 5from flask import Flask, render_template, request
 6app = Flask(__name__)
 7#define LED GPIO
 8ledRed = "P9_14"
 9pin = '50' #  P9_14 is gpio 50
10GPIOPATH="/sys/class/gpio"
11
12#initialize GPIO status variable
13ledRedSts = 0
14# Make sure pin is exported
15if (not os.path.exists(GPIOPATH+"/gpio"+pin)):
16    f = open(GPIOPATH+"/export", "w")
17    f.write(pin)
18    f.close()
19# Define led pin as output
20f = open(GPIOPATH+"/gpio"+pin+"/direction", "w")
21f.write("out")
22f.close()
23# turn led OFF 
24f = open(GPIOPATH+"/gpio"+pin+"/value", "w")
25f.write("0")
26f.close()
27
28@app.route("/")
29def index():
30	# Read Sensors Status
31	f = open(GPIOPATH+"/gpio"+pin+"/value", "r")
32	ledRedSts = f.read()
33	f.close()
34	templateData = {
35              'title' : 'GPIO output Status!',
36              'ledRed'  : ledRedSts,
37        }
38	return render_template('index3.html', **templateData)
39	
40@app.route("/<deviceName>/<action>")
41def action(deviceName, action):
42	if deviceName == 'ledRed':
43		actuator = ledRed
44	f = open(GPIOPATH+"/gpio"+pin+"/value", "w")
45	if action == "on":
46		f.write("1")
47	if action == "off":
48		f.write("0")
49	f.close()
50		     
51	f = open(GPIOPATH+"/gpio"+pin+"/value", "r")
52	ledRedSts = f.read()
53	f.close()
54
55	templateData = {
56              'ledRed'  : ledRedSts,
57	}
58	return render_template('index3.html', **templateData)
59if __name__ == "__main__":
60   app.run(host='0.0.0.0', port=8080, debug=True)

app3.py

What we have new on above code is the new “route”:

@app.route(“/<deviceName>/<action>”)

From the webpage, calls will be generated with the format:

http://192.168.7.2:8081/ledRed/on

or

http://192.168.7.2:8081/ledRed/off

For the above example, ledRed is the “deviceName” and on or off are examples of possible “action”. Those routes will be identified and properly “worked”. The main steps are:

  • Convert the string “ledRED”, for example, on its equivalent GPIO pin. The integer variable ledRed is equivalent to P9_14. We store this value on variable “actuator”

  • For each actuator, we will analyze the “action”, or “command” and act properly. If “action = on” for example, we must use the command: f.write("1")

  • Update the status of each actuator

  • Return the data to index.html

Let’s now create an index.html to show the GPIO status of each actuator and more importantly, create “buttons” to send the commands:

Listing 62 A simple Flask-based web server to write a GPIO (index3.html)#
 1<!DOCTYPE html>
 2   <head>
 3      <title>GPIO Control</title>
 4      <link rel="stylesheet" href='../static/style.css'/>
 5   </head>
 6   <body>
 7		<h2>Actuators</h2>
 8		<h3> Status </h3>
 9		     RED LED ==>  {{ ledRed  }}
10		<br>
11		<h3> Commands </h3>
12			RED LED Ctrl ==> 
13			<a href="/ledRed/on" class="button">TURN ON</a>  
14			<a href="/ledRed/off"class="button">TURN OFF</a>
15   </body>
16</html>

index3.html

bone$ ./app3.py

Point your browser as before and you will see:

Status of a GPIO pin on a web page

Control LED

Try clicking the “TURN ON” and “TURN OFF” buttons and your LED will respond.

app4.py and app5.py combine the previous apps. Try them out.

app4.py app5.py

Plotting Data#

Problem#

You have live, continuous, data coming into your Bone via one of the Analog Ins, and you want to plot it.

Solution#

Analog in - Continuous#

(This is based on information at: http://software-dl.ti.com/processor-sdk-linux/esd/docs/latest/linux/Foundational_Components/Kernel/Kernel_Drivers/ADC.html#Continuous%20Mode)

Reading a continuous analog signal requires some set up. First go to the iio devices directory.

bone$ cd /sys/bus/iio/devices/iio:device0
bone$ ls -F
buffer/  in_voltage0_raw  in_voltage2_raw  in_voltage4_raw  in_voltage6_raw  name      power/          subsystem@
dev      in_voltage1_raw  in_voltage3_raw  in_voltage5_raw  in_voltage7_raw  of_node@  scan_elements/  uevent

Here you see the files used to read the one shot values. Look in scan_elements to see how to enable continuous input.

bone$ ls scan_elements
in_voltage0_en     in_voltage1_index  in_voltage2_type   in_voltage4_en     in_voltage5_index  in_voltage6_type
in_voltage0_index  in_voltage1_type   in_voltage3_en     in_voltage4_index  in_voltage5_type   in_voltage7_en
in_voltage0_type   in_voltage2_en     in_voltage3_index  in_voltage4_type   in_voltage6_en     in_voltage7_index
in_voltage1_en     in_voltage2_index  in_voltage3_type   in_voltage5_en     in_voltage6_index  in_voltage7_type
Here you see three values for each analog input, _en (enable),

_index (index of this channel in the buffer’s chunks) and _type (how the ADC stores its data). (See the link above for details.) Let’s use the input at P9.40 which is AIN1. To enable this input:

bone$ echo 1 > scan_elements/in_voltage1_en

Next set the buffer size.

bone$ ls buffer
data_available  enable  length  watermark

Let’s use a 512 sample buffer. You might need to experiment with this.

bone$ echo 512 > buffer/length

Then start it running.
bone$ echo 1 > buffer/enable

Now, just read from */dev/iio:device0*.
1KHz sine wave sampled at 8KHz

Fig. 633 1KHz sine wave sampled at 8KHz#

An example Python program that does the above and reads and plots the buffer is analogInContinuous.py.

Listing 63 Code to read and plot a continuous analog input(analogInContinuous.py)#
 1#!/usr/bin/python
 2#//////////////////////////////////////
 3#	analogInContinuous.py
 4# 	Read analog data via IIO continuous mode and plots it.
 5#//////////////////////////////////////
 6# From: https://stackoverflow.com/questions/20295646/python-ascii-plots-in-terminal
 7# https://github.com/dkogan/gnuplotlib
 8# https://github.com/dkogan/gnuplotlib/blob/master/guide/guide.org
 9# sudo apt install gnuplot  (10 minute to install)
10# sudo apt install libatlas-base-dev
11# pip3 install gnuplotlib
12# This uses X11, so when connecting to the bone from the host use:  ssh -X bone
13
14# See https://elinux.org/index.php?title=EBC_Exercise_10a_Analog_In#Analog_in_-_Continuous.2C_Change_the_sample_rate
15# for instructions on changing the sampling rate.  Can go up to 200KHz.
16
17fd = open(IIODEV, "r")
18import numpy      as np
19import gnuplotlib as gp
20import time
21# import struct
22
23IIOPATH='/sys/bus/iio/devices/iio:device0'
24IIODEV='/dev/iio:device0'
25LEN = 100
26SAMPLERATE=8000
27AIN='2'
28
29# Setup IIO for Continous reading
30# Enable AIN
31try:
32    file1 = open(IIOPATH+'/scan_elements/in_voltage'+AIN+'_en', 'w')
33    file1.write('1') 
34    file1.close()
35except:     # carry on if it's already enabled
36    pass
37# Set buffer length
38file1 = open(IIOPATH+'/buffer/length', 'w')
39file1.write(str(2*LEN))     # I think LEN is in 16-bit values, but here we pass bytes
40file1.close()
41# Enable continuous
42file1 = open(IIOPATH+'/buffer/enable', 'w')
43file1.write('1')
44file1.close()
45
46x = np.linspace(0, 1000*LEN/SAMPLERATE, LEN)
47# Do a dummy plot to give time of the fonts to load.
48gp.plot(x, x)
49print("Waiting for fonts to load")
50time.sleep(10)
51
52print('Hit ^C to stop')
53
54fd = open(IIODEV, "r")
55
56try:
57    while True:
58        y = np.fromfile(fd, dtype='uint16', count=LEN)*1.8/4096
59        # print(y)
60        gp.plot(x, y,
61            xlabel = 't (ms)',
62            ylabel = 'volts',
63            _yrange = [0, 2],
64            title  = 'analogInContinuous',
65            legend = np.array( ("P9.39", ), ),
66            # ascii=1,
67            # terminal="xterm",
68            # legend = np.array( ("P9.40", "P9.38"), ),
69            # _with  = 'lines'
70            )
71
72except KeyboardInterrupt:
73    print("Turning off input.")
74    # Disable continuous
75    file1 = open(IIOPATH+'/buffer/enable', 'w')
76    file1.write('0')
77    file1.close()
78    
79    file1 = open(IIOPATH+'/scan_elements/in_voltage'+AIN+'_en', 'w')
80    file1.write('0') 
81    file1.close()
82
83# // Bone  | Pocket | AIN
84# // ----- | ------ | --- 
85# // P9_39 | P1_19  | 0
86# // P9_40 | P1_21  | 1
87# // P9_37 | P1_23  | 2
88# // P9_38 | P1_25  | 3
89# // P9_33 | P1_27  | 4
90# // P9_36 | P2_35  | 5
91# // P9_35 | P1_02  | 6

analogInContinuous.py

Be sure to read the instillation instructions in the comments. Also note this uses X windows and you need to ssh -X 192.168.7.2 for X to know where the display is.

Run it:

host$ ssh -X bone

bone$ cd beaglebone-cookbook-code/06iot
bone$ ./analogInContinuous.py
Hit ^C to stop

Todo

verify this works. fonts are taking too long to load

1KHz sine wave sampled at 8KHz is the output of a 1KHz sine wave.

It’s a good idea to disable the buffer when done.

bone$ echo 0 > /sys/bus/iio/devices/iio:device0/buffer/enable

Analog in - Continuous, Change the sample rate#

The built in ADCs sample at 8k samples/second by default. They can run as fast as 200k samples/second by editing a device tree.

bone$ cd /opt/source/bb.org-overlays
bone$ make

This will take a while the first time as it compiles all the device trees.

bone$ vi src/arm/src/arm/BB-ADC-00A0.dts

Around line 57 you’ll see

Line    Code
57     // For each step, number of adc clock cycles to wait between setting up muxes and sampling.
58     //  range: 0 .. 262143
59     //  optional, default is 152 (XXX but why?!)
60     ti,chan-step-opendelay = <152 152 152 152 152 152 152 152>;
61     //`
62     // XXX is there any purpose to set this nonzero other than to fine-tune the sample rate?
63
64
65     // For each step, how many times it should sample to average.
66     //  range: 1 .. 16, must be power of two (i.e. 1, 2, 4, 8, or 16)
67     //  optional, default is 16
68     ti,chan-step-avg = <16 16 16 16 16 16 16 16>;

The comments give lots of details on how to adjust the device tree to change the sample rate. Line 68 says for every sample returned, average 16 values. This will give you a cleaner signal, but if you want to go fast, change the 16’s to 1’s. Line 60 says to delay 152 cycles between each sample. Set this to 0 to got as fast a possible.

ti,chan-step-avg = <1 1 1 1 1 1 1 1>;
ti,chan-step-opendelay = <0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00>;

Now compile it.

bone$ make
  DTC     src/arm/BB-ADC-00A0.dtbo
gcc -o config-pin ./tools/pmunts_muntsos/config-pin.c

It knows to only recompile the file you just edited. Now install and reboot.

bone$ sudo make install
...
'src/arm/AM335X-PRU-UIO-00A0.dtbo' -> '/lib/firmware/AM335X-PRU-UIO-00A0.dtbo'
'src/arm/BB-ADC-00A0.dtbo' -> '/lib/firmware/BB-ADC-00A0.dtbo'
'src/arm/BB-BBBMINI-00A0.dtbo' -> '/lib/firmware/BB-BBBMINI-00A0.dtbo'
...
bone$ reboot

A number of files get installed, including the ADC file. Now try rerunning.

bone$ cd beaglebone-cookbook-code/06iot
bone$ ./analogInContinuous.py
Hit ^C to stop

Here’s the output of a 10KHz triangle wave.

Todo

Is this true: (The plot is wrong, but eLinux won’t let me fix it.)

10KHz triangle wave sampled at 200KHz

Fig. 634 10KHz triangle wave sampled at 200KHz#

It’s still a good idea to disable the buffer when done.

bone$ echo 0 > /sys/bus/iio/devices/iio:device0/buffer/enable

Sending an Email#

Problem#

You want to send an email via Gmail from the Bone.

Solution#

This example came from https://realpython.com/python-send-email/. First, you need to set up a Gmail account, if you don’t already have one. Then add the code in Sending email using nodemailer (emailtTest.py) to a file named emailTest.py. Substitute your own Gmail username. For the password:

Listing 64 Sending email using nodemailer (emailtTest.py)#
 1#!/usr/bin/env python
 2# From: https://realpython.com/python-send-email/
 3import smtplib, ssl
 4
 5port = 587  # For starttls
 6smtp_server  = "smtp.gmail.com"
 7sender_email = "from_account@gmail.com"
 8receiver_email = "to_account@gmail.com"
 9# Go to: https://myaccount.google.com/security
10# Select App password
11# Generate your own 16 char password, copy here
12# Delete password when done
13password = "cftqhcejjdjfdwjh"
14message = """\
15Subject: Testing email
16
17This message is sent from Python.
18
19"""
20context = ssl.create_default_context()
21with smtplib.SMTP(smtp_server, port) as server:
22    server.starttls(context=context)
23    server.login(sender_email, password)
24    server.sendmail(sender_email, receiver_email, message)

emailTest.py

Then run the script to send the email:

bone$ chmod *x emailTest.py
bone$ .\emailTest.py

Warning

This solution requires your Gmail password to be in plain text in a file, which is a security problem. Make sure you know who has access to your Bone. Also, if you remove the microSD card, make sure you know who has access to it. Anyone with your microSD card can read your Gmail password.

Be careful about putting this into a loop. Gmail presently limits you to 500 emails per day and 10 MB per message.

See https://realpython.com/python-send-email/ for an example that sends an attached file.

Sending an SMS Message#

Todo

My twilio account is suspended, using yoder@rose-hulman.edu.

Problem#

You want to send a text message from BeagleBone Black.

Solution#

There are a number of SMS services out there. This recipe uses Twilio because you can use it for free, but you will need to verify the number to which you are texting. First, go to Twilio’s home page and set up an account. Note your account SID and authorization token. If you are using the free version, be sure to verify your numbers.

Next, install Trilio by using the following command for python:

bone$ sudo apt install python-pip
bone$ sudo pip install twilio

or for Javascript:

bone$ npm install -g twilio

Finally, add the code in Sending SMS messages using Twilio (twilioTest.py) to a file named twilioTest.py and run it. Your text will be sent.

Listing 65 Sending SMS messages using Twilio (twilioTest.py)#
 1#!/usr/bin/env python
 2# Download the helper library from https://www.twilio.com/docs/python/install
 3import os
 4from twilio.rest import Client
 5
 6
 7# Find your Account SID and Auth Token at twilio.com/console
 8# and set the environment variables. See http://twil.io/secure
 9account_sid = os.environ['TWILIO_ACCOUNT_SID']
10auth_token = os.environ['TWILIO_AUTH_TOKEN']
11client = Client(account_sid, auth_token)
12
13message = client.messages \
14                .create(
15                     body="Join Earth's mightiest heroes. Like Kevin Bacon.",
16                     from_='+18122333219',
17                     to='+18122333219'
18                 )
19
20print(message.sid)

twilioTest.py

Listing 66 Sending SMS messages using Twilio (twilio-test.js)#
 1#!/usr/bin/env node
 2// From: http://twilio.github.io/twilio-node/
 3// Twilio Credentials 
 4var accountSid = ''; 
 5var authToken = ''; 
 6 
 7//require the Twilio module and create a REST client 
 8var client = require('twilio')(accountSid, authToken); 
 9 
10client.messages.create({ 
11	to: "812555121", 
12	from: "+2605551212", 
13	body: "This is a test",   
14}, function(err, message) { 
15	console.log(message.sid); 
16});
17
18// https://github.com/twilio/twilio-node/blob/master/LICENSE

twilio-test.js

Twilio allows a small number of free text messages, enough to test your code and to play around some.

Displaying the Current Weather Conditions#

Problem#

You want to display the current weather conditions.

Solution#

Because your Bone is on the network, it’s not hard to access the current weather conditions from a weather API.

bash$ export APPID="Your key"
Listing 67 Code for getting current weather conditions (weather.py)#
 1#!/usr/bin/env python3
 2# Displays current weather and forecast
 3import os
 4import sys
 5from datetime import datetime
 6import requests     # For getting weather
 7                
 8# http://api.openweathermap.org/data/2.5/onecall
 9params = {
10    'appid': os.environ['APPID'],
11    # 'city': 'brazil,indiana',
12    'exclude': "minutely,hourly",
13    'lat':  '39.52',
14    'lon': '-87.12',
15    'units': 'imperial'
16    }
17urlWeather = "http://api.openweathermap.org/data/2.5/onecall"
18
19print("Getting weather")
20
21try:
22    r = requests.get(urlWeather, params=params)
23    if(r.status_code==200):
24        # print("headers: ", r.headers)
25        # print("text: ", r.text)
26        # print("json: ", r.json())
27        weather = r.json()
28        print("Temp: ", weather['current']['temp'])         # ①
29        print("Humid:", weather['current']['humidity'])
30        print("Low:  ", weather['daily'][1]['temp']['min'])
31        print("High: ", weather['daily'][0]['temp']['max'])
32        day = weather['daily'][0]['sunrise']-weather['timezone_offset']
33        print("sunrise: " + datetime.utcfromtimestamp(day).strftime('%Y-%m-%d %H:%M:%S'))
34        # print("Day: " + datetime.utcfromtimestamp(day).strftime('%a'))
35        # print("weather: ", weather['daily'][1])           # ②
36        # print("weather: ", weather)                       # ③
37        # print("icon: ", weather['current']['weather'][0]['icon'])
38        # print()
39
40    else:
41        print("status_code: ", r.status_code)
42except IOError:
43    print("File not found: " + tmp101)
44    print("Have you run setup.sh?")
45except:
46    print("Unexpected error:", sys.exc_info())

weather.py

  1. Prints current conditions.

  2. Prints the forecast for the next day.

  3. Prints everything returned by the weather site.

Uncomment what you want to be displayed.

Run this by using the following commands:

bone$ ./weather.py
Getting weather
Temp:  73.72
Humid: 31
Low:   54.21
High:  75.47
sunrise: 2023-06-09 14:21:07

The weather API returns lots of information. Use Python to extract the information you want.

Sending and Receiving Tweets#

Problem#

You want to send and receive tweets (Twitter posts) with your Bone.

Solution#

Twitter has a whole git repo of sample code for interacting with Twitter. Here I’ll show how to create a tweet and then how to delete it.

Creating a Project and App#

  • Follow the directions here to create a project and app.

  • Be sure to give your app Read and Write permission.

  • Then go to the developer portal and select you app by clicking on the gear icon to the right of the app name.

  • Click on the Keys and tokens tab. Here you can get to all your keys and tokens.

Tip

Be sure to record them, you can’t get them later.

  • Open the file twitterKeys.sh and record your keys in it.

export API_KEY='XXX'
export API_SECRET_KEY='XXX'
export BEARER_TOKEN='XXX'
export TOKEN='XXXX'
export TOKEN_SECRET='XXX'
  • Next, source the file so the values will appear in your bash session.

bash$ source twitterKeys.sh

You’ll need to do this every time you open a new bash window.

Creating a tweet#

Add the code in Create a Tweet (twitter_create_tweet.py) to a file called twitter_create_tweet_.py and run it to see your timeline.

Listing 68 Create a Tweet (twitter_create_tweet.py)#
 1#!/usr/bin/env python
 2# From: https://github.com/twitterdev/Twitter-API-v2-sample-code/blob/main/Manage-Tweets/create_tweet.py
 3from requests_oauthlib import OAuth1Session
 4import os
 5import json
 6
 7# In your terminal please set your environment variables by running the following lines of code.
 8# export 'API_KEY'='<your_consumer_key>'
 9# export 'API_SECRET_KEY'='<your_consumer_secret>'
10
11consumer_key = os.environ.get("API_KEY")
12consumer_secret = os.environ.get("API_SECRET_KEY")
13
14# Be sure to add replace the text of the with the text you wish to Tweet. You can also add parameters to post polls, quote Tweets, Tweet with reply settings, and Tweet to Super Followers in addition to other features.
15payload = {"text": "Hello world!"}
16
17# Get request token
18request_token_url = "https://api.twitter.com/oauth/request_token?oauth_callback=oob&x_auth_access_type=write"
19oauth = OAuth1Session(consumer_key, client_secret=consumer_secret)
20
21try:
22    fetch_response = oauth.fetch_request_token(request_token_url)
23except ValueError:
24    print(
25        "There may have been an issue with the consumer_key or consumer_secret you entered."
26    )
27
28resource_owner_key = fetch_response.get("oauth_token")
29resource_owner_secret = fetch_response.get("oauth_token_secret")
30print("Got OAuth token: %s" % resource_owner_key)
31
32# Get authorization
33base_authorization_url = "https://api.twitter.com/oauth/authorize"
34authorization_url = oauth.authorization_url(base_authorization_url)
35print("Please go here and authorize: %s" % authorization_url)
36verifier = input("Paste the PIN here: ")
37
38# Get the access token
39access_token_url = "https://api.twitter.com/oauth/access_token"
40oauth = OAuth1Session(
41    consumer_key,
42    client_secret=consumer_secret,
43    resource_owner_key=resource_owner_key,
44    resource_owner_secret=resource_owner_secret,
45    verifier=verifier,
46)
47oauth_tokens = oauth.fetch_access_token(access_token_url)
48
49access_token = oauth_tokens["oauth_token"]
50access_token_secret = oauth_tokens["oauth_token_secret"]
51
52# Make the request
53oauth = OAuth1Session(
54    consumer_key,
55    client_secret=consumer_secret,
56    resource_owner_key=access_token,
57    resource_owner_secret=access_token_secret,
58)
59
60# Making the request
61response = oauth.post(
62    "https://api.twitter.com/2/tweets",
63    json=payload,
64)
65
66if response.status_code != 201:
67    raise Exception(
68        "Request returned an error: {} {}".format(response.status_code, response.text)
69    )
70
71print("Response code: {}".format(response.status_code))
72
73# Saving the response as JSON
74json_response = response.json()
75print(json.dumps(json_response, indent=4, sort_keys=True))

twitter_create_tweet.py

Run the code and you’ll have to authorize.

bash$ ./twitter_create_tweet.py
Got OAuth token: tWBldQAAAAAAWBJgAAABggJt7qg
Please go here and authorize: https://api.twitter.com/oauth/authorize?oauth_token=tWBldQAAAAAAWBJgAAABggJt7qg
Paste the PIN here: 4859044
Response code: 201
{
    "data": {
        "id": "1547963178700533760",
        "text": "Hello world!"
    }
}

Check your twitter account and you’ll see the new tweet. Record the id number and we’ll use it next to delete the tweet.

Deleting a tweet#

Use the code in Code to delete a tweet (twitter_delete_tweet.py) to delete a tweet. Around line 15 is the id number. Paste in the value returned above.

Listing 69 Code to delete a tweet (twitter_delete_tweet.py)#
 1#!/usr/bin/env python
 2# From: https://github.com/twitterdev/Twitter-API-v2-sample-code/blob/main/Manage-Tweets/delete_tweet.py
 3from requests_oauthlib import OAuth1Session
 4import os
 5import json
 6
 7# In your terminal please set your environment variables by running the following lines of code.
 8# export 'API_KEY'='<your_consumer_key>'
 9# export 'API_SECRET_KEY'='<your_consumer_secret>'
10
11consumer_key = os.environ.get("API_KEY")
12consumer_secret = os.environ.get("API_SECRET_KEY")
13
14# Be sure to replace tweet-id-to-delete with the id of the Tweet you wish to delete. The authenticated user must own the list in order to delete
15id = "1547963178700533760"
16
17# Get request token
18request_token_url = "https://api.twitter.com/oauth/request_token?oauth_callback=oob&x_auth_access_type=write"
19oauth = OAuth1Session(consumer_key, client_secret=consumer_secret)
20
21try:
22    fetch_response = oauth.fetch_request_token(request_token_url)
23except ValueError:
24    print(
25        "There may have been an issue with the consumer_key or consumer_secret you entered."
26    )
27
28resource_owner_key = fetch_response.get("oauth_token")
29resource_owner_secret = fetch_response.get("oauth_token_secret")
30print("Got OAuth token: %s" % resource_owner_key)
31
32# Get authorization
33base_authorization_url = "https://api.twitter.com/oauth/authorize"
34authorization_url = oauth.authorization_url(base_authorization_url)
35print("Please go here and authorize: %s" % authorization_url)
36verifier = input("Paste the PIN here: ")
37
38# Get the access token
39access_token_url = "https://api.twitter.com/oauth/access_token"
40oauth = OAuth1Session(
41    consumer_key,
42    client_secret=consumer_secret,
43    resource_owner_key=resource_owner_key,
44    resource_owner_secret=resource_owner_secret,
45    verifier=verifier,
46)
47oauth_tokens = oauth.fetch_access_token(access_token_url)
48
49access_token = oauth_tokens["oauth_token"]
50access_token_secret = oauth_tokens["oauth_token_secret"]
51
52# Make the request
53oauth = OAuth1Session(
54    consumer_key,
55    client_secret=consumer_secret,
56    resource_owner_key=access_token,
57    resource_owner_secret=access_token_secret,
58)
59
60# Making the request
61response = oauth.delete("https://api.twitter.com/2/tweets/{}".format(id))
62
63if response.status_code != 200:
64    raise Exception(
65        "Request returned an error: {} {}".format(response.status_code, response.text)
66    )
67
68print("Response code: {}".format(response.status_code))
69
70# Saving the response as JSON
71json_response = response.json()
72print(json_response)

twitter_delete_tweet.py

Todo

Start Here. Update for python.

The code in Tweet when a button is pushed (twitterPushbutton.js) sends a tweet whenever a button is pushed.

Listing 70 Tweet when a button is pushed (twitterPushbutton.js)#
 1#!/usr/bin/env node
 2// From: https://www.npmjs.org/package/node-twitter
 3// Tweets with attached image media (JPG, PNG or GIF) can be posted 
 4// using the upload API endpoint.
 5var Twitter = require('node-twitter');
 6var b = require('bonescript');
 7var key = require('./twitterKeys');
 8var gpio = "P9_42";
 9var count = 0;
10
11b.pinMode(gpio, b.INPUT);
12b.attachInterrupt(gpio, sendTweet, b.FALLING);
13
14var twitterRestClient = new Twitter.RestClient(
15    key.API_KEY, key.API_SECRET,
16    key.TOKEN,   key.TOKEN_SECRET
17);
18
19function sendTweet() {
20    console.log("Sending...");
21    count++;
22
23    twitterRestClient.statusesUpdate(
24        {'status': 'Posting tweet ' + count + ' via my BeagleBone Black', },
25        function(error, result) {
26            if (error) {
27                console.log('Error: ' + 
28                    (error.code ? error.code + ' ' + error.message : error.message));
29            }
30    
31            if (result) {
32                console.log(result);
33            }
34        }
35    );
36}
37
38// node-twitter is made available under terms of the BSD 3-Clause License.
39// http://www.opensource.org/licenses/BSD-3-Clause

twitterPushbutton.js

To see many other examples, go to Twitter for Node.js on NPMJS.com.

This opens up many new possibilities. You can read a temperature sensor and tweet its value whenever it changes, or you can turn on an LED whenever a certain hashtag is used. What are you going to tweet?

Wiring the IoT with Node-RED#

Problem#

You want BeagleBone to interact with the Internet, but you want to program it graphically.

Solution#

Node-RED is a visual tool for wiring the IoT. It makes it easy to turn on a light when a certain hashtag is tweeted, or spin a motor if the forecast is for hot weather.

Starting Node-RED#

Node-RED is already installed, to run Node-RED, use the following command to start.

bone$ sudo systemctl start nodered

Or run the following to have Node-RED start everytime you reboot.

bone$ sudo systemctl enable --now nodered

Node-RED is listening on part 1880. Point your browser to http://192.168.7.2:1880, and you will see the screen shown in The Node-RED web page.

node-red

Fig. 635 The Node-RED web page#

Building a Node-RED Flow#

The example in this recipe builds a Node-RED flow that will toggle an LED whenever a certain hashtag is tweeted. But first, you need to set up the Node-RED flow with the twitter node:

  • On the Node-RED web page, scroll down until you see the social nodes on the left side of the page.

  • Drag the twitter node to the canvas, as shown in Node-RED twitter node.

node-red

Fig. 636 Node-RED twitter node#

Authorize Twitter by double-clicking the twitter node. You’ll see the screen shown in Node-RED Twitter authorization, step 1.

node-red authentication

Fig. 637 Node-RED Twitter authorization, step 1#

Click the pencil button to bring up the dialog box shown in Node-RED twitter authorization, step 2.

node-red authentication2

Fig. 638 Node-RED twitter authorization, step 2#

node-red authentication3

Fig. 639 Node-RED Twitter site authorization#

node-red beagle hash

Fig. 640 Node-RED adding the #BeagleBone hashtag#

node-red debug

Fig. 641 Node-RED Twitter adding debug node and connecting#

  • In the right panel, in the upper-right corner, click the “debug” tab.

  • Finally, click the Deploy button above the “debug” tab.

Your Node-RED flow is now running on the Bone. Test it by going to Twitter and tweeting something with the hashtag #BeagleBone. Your Bone is now responding to events happening out in the world.

Adding an LED Toggle#

Now, we’re ready to add the LED toggle:

node-red discrete out node

Fig. 642 Node-RED adding bbb-discrete-out node#

Double-click the node, select your GPIO pin and “Toggle state,” and then set “Startup as” to 1 (Node-RED adding bbb-discrete-out configuration).

node-red discrete out setup

Fig. 643 Node-RED adding bbb-discrete-out configuration#

Click Ok and then Deploy.

Test again. The LED will toggle every time the hashtag #BeagleBone is tweeted. With a little more exploring, you should be able to have your Bone ringing a bell or spinning a motor in response to tweets.

Communicating over a Serial Connection to an Arduino or LaunchPad#

Problem#

You would like your Bone to talk to an Arduino or LaunchPad.

Solution#

The common serial port (also known as a UART) is the simplest way to talk between the two. Wire it up as shown in Wiring a LaunchPad to a Bone via the common serial port.

Warning

BeagleBone Black runs at 3.3 V. When wiring other devices to it, ensure that they are also 3.3 V. The LaunchPad I’m using is 3.3 V, but many Arduinos are 5.0 V and thus won’t work. Or worse, they might damage your Bone.

MSP430 LaunchPad

Fig. 644 Wiring a LaunchPad to a Bone via the common serial port#

Add the code (or sketch, as it’s called in Arduino-speak) in LaunchPad code for communicating via the UART (launchPad.ino) to a file called launchPad.ino and run it on your LaunchPad.

Listing 71 LaunchPad code for communicating via the UART (launchPad.ino)#
 1/*
 2  Tests connection to a BeagleBone
 3  Mark A. Yoder
 4  Waits for input on Serial Port
 5  g - Green toggle
 6  r - Red toggle
 7*/
 8char inChar = 0; // incoming serial byte
 9int red = 0;
10int green = 0;
11
12void setup()
13{
14  // initialize the digital pin as an output.
15  pinMode(RED_LED, OUTPUT);           // ①
16  pinMode(GREEN_LED, OUTPUT);  
17  // start serial port at 9600 bps:
18  Serial.begin(9600);                 // ②
19  Serial.print("Command (r, g): ");   // ③
20  
21  digitalWrite(GREEN_LED, green);     // ④
22  digitalWrite(  RED_LED, red);
23}
24
25void loop()
26{
27  if(Serial.available() > 0 ) {       // ⑤
28    inChar = Serial.read();
29    switch(inChar) {                  // ⑥
30      case 'g':
31        green = ~green;
32        digitalWrite(GREEN_LED, green);
33        Serial.println("Green");
34        break;
35      case 'r':
36        red = ~red;
37        digitalWrite(RED_LED, red);
38        Serial.println("Red");
39        break;
40    }
41    Serial.print("Command (r, g): ");
42  }
43}
44

launchPad.ino

① Set the mode for the built-in red and green LEDs.

② Start the serial port at 9600 baud.

③ Prompt the user, which in this case is the Bone.

④ Set the LEDs to the current values of the red and green variables.

⑤ Wait for characters to arrive on the serial port.

⑥ After the characters are received, read it and respond to it.

On the Bone, add the script in Code for communicating via the UART (launchPad.js) to a file called launchPad.js and run it.

Listing 72 Code for communicating via the UART (launchPad.js)#
 1#!/usr/bin/env node
 2// Need to add exports.serialParsers = m.module.parsers;
 3// to /usr/local/lib/node_modules/bonescript/serial.js
 4var b = require('bonescript');
 5
 6var port = '/dev/ttyO1';                    // ①
 7var options = {
 8    baudrate: 9600,                         // ②
 9    parser: b.serialParsers.readline("\n")  // ③
10};
11
12b.serialOpen(port, options, onSerial);      // ④
13
14function onSerial(x) {                      // ⑤
15    console.log(x.event);
16    if (x.err) {
17        console.log('***ERROR*** ' + JSON.stringify(x));
18    }
19    if (x.event == 'open') {
20        console.log('***OPENED***');
21        setInterval(sendCommand, 1000);     // ⑥
22    }
23    if (x.event == 'data') {
24        console.log(String(x.data));
25    }
26}
27
28var command = ['r', 'g'];                   // ⑦
29var commIdx = 1;
30
31function sendCommand() {
32    // console.log('Command: ' + command[commIdx]);
33    b.serialWrite(port, command[commIdx++]); // ⑧
34    if(commIdx >= command.length) {         // ⑨
35        commIdx = 0;
36    }
37}

launchPad.js

① Select which serial port to use. Table of UART outputs sows what’s available. We’ve wired P9_24 and P9_26, so we are using serial port /dev/ttyO1. (Note that’s the letter O and not the number zero.)

② Set the baudrate to 9600, which matches the setting on the LaunchPad.

③ Read one line at a time up to the newline character (n).

④ Open the serial port and call onSerial() whenever there is data available.

⑤ Determine what event has happened on the serial port and respond to it.

⑥ If the serial port has been opened, start calling sendCommand() every 1000 ms.

⑦ These are the two commands to send.

⑧ Write the character out to the serial port and to the LaunchPad.

⑨ Move to the next command.

UART outputs

Fig. 645 Table of UART outputs#

Discussion#

When you run the script in Code for communicating via the UART (launchPad.js), the Bone opens up the serial port and every second sends a new command, either r or g. The LaunchPad waits for the command, when it arrives, responds by toggling the corresponding LED.