Internet of Things
Introduction
You can easily connect BeagleBone Black to the Internet via a wire ([networking_wired]), wirelessly ([networking_wireless]), or through the USB to a host and then to the Internet ([networking_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.
Discussion
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.
<!DOCTYPE html>
<html>
<body>
<h1>My First Heading</h1>
<p>My first paragraph.</p>
</body>
</html>
You will see the web page shown in test.html as served by nginx.
![test.html served by nginx](figures/nginxTest.png)
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://github.com/MarkAYoder/BoneCookbook bone$ cd BoneCookbook/doc/06iod/code/flash
First Flask - hello, world
Our first example is helloWorld.py
#!/usr/bin/env python
# From: https://towardsdatascience.com/python-webserver-with-flask-and-raspberry-pi-398423cc6f5d
from flask import Flask # (1)
app = Flask(__name__) # (2)
@app.route('/') # (3)
def index():
return 'hello, world'
if __name__ == '__main__':
app.run(debug=True, port=8080, host='0.0.0.0')# (4)
-
The first line loads the Flask module into your Python script.
-
The second line creates a Flask object called app.
-
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.
-
The last line says to “listen” on port 8080, reporting any errors.
Now on your host computer, browse to 192.168.7.2:8080 and you should see.
![Test page](figures/flaskServer.png)
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:
<!DOCTYPE html>
<head>
<title>{{ title }}</title>
</head>
<body>
<h1>Hello, World!</h1>
<h2>The date and time on the server is: {{ time }}</h2>
</body>
</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:
#!/usr/bin/env python
# From: https://towardsdatascience.com/python-webserver-with-flask-and-raspberry-pi-398423cc6f5d
'''
Code created by Matt Richardson
for details, visit: http://mattrichardson.com/Raspberry-Pi-Flask/inde...
'''
from flask import Flask, render_template
import datetime
app = Flask(__name__)
@app.route("/")
def hello():
now = datetime.datetime.now()
timeString = now.strftime("%Y-%m-%d %H:%M")
templateData = {
'title' : 'HELLO!',
'time': timeString
}
return render_template('index1.html', **templateData)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=8080, debug=True)
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 index.html template to the web browser using the variables in the templateData dictionary.
Execute the Python script:
bone$ .\app1.py
Open any web browser and browse to 192.168.7.2:8080. You should see:
![app1.py](figures/flaskapp1.png)
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 second.
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 [js_pushbutton_fig].
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.
#!/usr/bin/env python
# From: https://towardsdatascience.com/python-webserver-with-flask-and-raspberry-pi-398423cc6f5d
import os
from flask import Flask, render_template
app = Flask(__name__)
# gpioinfo | grep -e chip -e P9_11
pin = '30' # P9_11 is gpio 30
GPIOPATH="/sys/class/gpio"
buttonSts = 0
# Make sure pin is exported
if (not os.path.exists(GPIOPATH+"/gpio"+pin)):
f = open(GPIOPATH+"/export", "w")
f.write(pin)
f.close()
# Make it an input pin
f = open(GPIOPATH+"/gpio"+pin+"/direction", "w")
f.write("in")
f.close()
@app.route("/")
def index():
# Read Button Status
f = open(GPIOPATH+"/gpio"+pin+"/value", "r")
buttonSts = f.read()[:-1]
f.close()
# buttonSts = GPIO.input(button)
templateData = {
'title' : 'GPIO input Status!',
'button' : buttonSts,
}
return render_template('index2.html', **templateData)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=8080, debug=True)
Look that 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:
<!DOCTYPE html>
<head>
<title>{{ title }}</title>
<link rel="stylesheet" href='../static/style.css'/>
</head>
<body>
<h1>{{ title }}</h1>
<h2>Button pressed: {{ button }}</h1>
</body>
</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 [networking_GPIOserver_fig].
![GPIO status](figures/flaskapp2.png)
Currently, the 0 shows that the button isn’t pressed. Try refreshing the page while pushing the button, and you will see 1 displayed.
Discussion
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.
The python script Let’s create a new Python script and named it app3.py.
#!/usr/bin/env python
# From: https://towardsdatascience.com/python-webserver-with-flask-and-raspberry-pi-398423cc6f5d
# import Adafruit_BBIO.GPIO as GPIO
import os
from flask import Flask, render_template, request
app = Flask(__name__)
#define LED GPIO
ledRed = "P9_14"
pin = '50' # P9_14 is gpio 50
GPIOPATH="/sys/class/gpio"
#initialize GPIO status variable
ledRedSts = 0
# Make sure pin is exported
if (not os.path.exists(GPIOPATH+"/gpio"+pin)):
f = open(GPIOPATH+"/export", "w")
f.write(pin)
f.close()
# Define led pin as output
f = open(GPIOPATH+"/gpio"+pin+"/direction", "w")
f.write("out")
f.close()
# turn led OFF
f = open(GPIOPATH+"/gpio"+pin+"/value", "w")
f.write("0")
f.close()
@app.route("/")
def index():
# Read Sensors Status
f = open(GPIOPATH+"/gpio"+pin+"/value", "r")
ledRedSts = f.read()
f.close()
templateData = {
'title' : 'GPIO output Status!',
'ledRed' : ledRedSts,
}
return render_template('index3.html', **templateData)
@app.route("/<deviceName>/<action>")
def action(deviceName, action):
if deviceName == 'ledRed':
actuator = ledRed
f = open(GPIOPATH+"/gpio"+pin+"/value", "w")
if action == "on":
f.write("1")
if action == "off":
f.write("0")
f.close()
f = open(GPIOPATH+"/gpio"+pin+"/value", "r")
ledRedSts = f.read()
f.close()
templateData = {
'ledRed' : ledRedSts,
}
return render_template('index3.html', **templateData)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=8080, debug=True)
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:
or
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
-
Update the variable library
-
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:
<!DOCTYPE html>
<head>
<title>GPIO Control</title>
<link rel="stylesheet" href='../static/style.css'/>
</head>
<body>
<h2>Actuators</h2>
<h3> Status </h3>
RED LED ==> {{ ledRed }}
<br>
<h3> Commands </h3>
RED LED Ctrl ==>
<a href="/ledRed/on" class="button">TURN ON</a>
<a href="/ledRed/off"class="button">TURN OFF</a>
</body>
</html>
bone$ ./app3.py
Point your browser as before and you will see:
![Control LED](figures/flaskapp3.png)
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.
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](figures/Sine1k.png)
An example Python program that does the above and the reads and plot the buffer is here: analogInContinuous.py
#!/usr/bin/python
#//////////////////////////////////////
# analogInContinuous.py
# Read analog data via IIO continous mode and plots it.
#//////////////////////////////////////
# From: https://stackoverflow.com/questions/20295646/python-ascii-plots-in-terminal
# https://github.com/dkogan/gnuplotlib
# https://github.com/dkogan/gnuplotlib/blob/master/guide/guide.org
# sudo apt install gnuplot (10 minute to install)
# sudo apt install libatlas-base-dev
# pip3 install gnuplotlib
# This uses X11, so when connecting to the bone from the host use: ssh -X bone
# See https://elinux.org/index.php?title=EBC_Exercise_10a_Analog_In#Analog_in_-_Continuous.2C_Change_the_sample_rate
# for instructions on changing the sampling rate. Can go up to 200KHz.
import numpy as np
import gnuplotlib as gp
import time
# import struct
IIOPATH='/sys/bus/iio/devices/iio:device0'
IIODEV='/dev/iio:device0'
LEN = 100
SAMPLERATE=8000
AIN='2'
# Setup IIO for Continous reading
# Enable AIN
try:
file1 = open(IIOPATH+'/scan_elements/in_voltage'+AIN+'_en', 'w')
file1.write('1')
file1.close()
except: # carry on if it's already enabled
pass
# Set buffer length
file1 = open(IIOPATH+'/buffer/length', 'w')
file1.write(str(2*LEN)) # I think LEN is in 16-bit values, but here we pass bytes
file1.close()
# Enable continous
file1 = open(IIOPATH+'/buffer/enable', 'w')
file1.write('1')
file1.close()
x = np.linspace(0, 1000*LEN/SAMPLERATE, LEN)
# Do a dummy plot to give time of the fonts to load.
gp.plot(x, x)
print("Waiting for fonts to load")
time.sleep(10)
print('Hit ^C to stop')
fd = open(IIODEV, "r")
try:
while True:
y = np.fromfile(fd, dtype='uint16', count=LEN)*1.8/4096
# print(y)
gp.plot(x, y,
xlabel = 't (ms)',
ylabel = 'volts',
_yrange = [0, 2],
title = 'analogInContinuous',
legend = np.array( ("P9.39", ), ),
# ascii=1,
# terminal="xterm",
# legend = np.array( ("P9.40", "P9.38"), ),
# _with = 'lines'
)
except KeyboardInterrupt:
print("Turning off input.")
# Disable continous
file1 = open(IIOPATH+'/buffer/enable', 'w')
file1.write('0')
file1.close()
file1 = open(IIOPATH+'/scan_elements/in_voltage'+AIN+'_en', 'w')
file1.write('0')
file1.close()
# // Bone | Pocket | AIN
# // ----- | ------ | ---
# // P9_39 | P1_19 | 0
# // P9_40 | P1_21 | 1
# // P9_37 | P1_23 | 2
# // P9_38 | P1_25 | 3
# // P9_33 | P1_27 | 4
# // P9_36 | P2_35 | 5
# // P9_35 | P1_02 | 6
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/doc/06iot/code bone$ ./analogInContinuous.py Hit ^C to stop
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
make 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/docs/06iot/code> bone$ ./analogInContinuous.py Hit ^C to stop
Here’s the output of a 10KHz triangle wave.
![10KHz triangle wave sampled at 200KHz](figures/Tri10k.png)
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 (emailTest.py) to a file named emailTest.py. Substitute your own Gmail username. For the password:
-
Select App password.
-
Generate your own 16 char password and copy it into emailTest.py.
-
Be sure to delete password when done https://myaccount.google.com/apppasswords .
#!/usr/bin/env python
# From: https://realpython.com/python-send-email/
import smtplib, ssl
port = 587 # For starttls
smtp_server = "smtp.gmail.com"
sender_email = "from_account@gmail.com"
receiver_email = "to_account@gmail.com"
# Go to: https://myaccount.google.com/security
# Select App password
# Generate your own 16 char password, copy here
# Delete password when done
password = "cftqhcejjdjfdwjh"
message = """\
Subject: Testing email
This message is sent from Python.
"""
context = ssl.create_default_context()
with smtplib.SMTP(smtp_server, port) as server:
server.starttls(context=context)
server.login(sender_email, password)
server.sendmail(sender_email, receiver_email, message)
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. |
Discussion
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.
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.
-
Go to https://openweathermap.org/ and create an account.
-
Go to https://home.openweathermap.org/api_keys and get your API key.
-
Store your key in the bash variable APPID.
bash$ export APPID="Your key"
-
Then add the code in Code for getting current weather conditions (weather.py) to a file named weather.js.
-
Run the pyhon script.
#!/usr/bin/env python3
# Displays current weather and forcast
import os
import sys
from datetime import datetime
import requests # For getting weather
# http://api.openweathermap.org/data/2.5/onecall
params = {
'appid': os.environ['APPID'],
# 'city': 'brazil,indiana',
'exclude': "minutely,hourly",
'lat': '39.52',
'lon': '-87.12',
'units': 'imperial'
}
urlWeather = "http://api.openweathermap.org/data/2.5/onecall"
print("Getting weather")
try:
r = requests.get(urlWeather, params=params)
if(r.status_code==200):
# print("headers: ", r.headers)
# print("text: ", r.text)
# print("json: ", r.json())
weather = r.json()
print("Temp: ", weather['current']['temp']) # (1)
print("Humid:", weather['current']['humidity'])
print("Low: ", weather['daily'][1]['temp']['min'])
print("High: ", weather['daily'][0]['temp']['max'])
day = weather['daily'][0]['sunrise']-weather['timezone_offset']
print("sunrise: " + datetime.utcfromtimestamp(day).strftime('%Y-%m-%d %H:%M:%S'))
# print("Day: " + datetime.utcfromtimestamp(day).strftime('%a'))
# print("weather: ", weather['daily'][1]) # (2)
# print("weather: ", weather) # (3)
# print("icon: ", weather['current']['weather'][0]['icon'])
# print()
else:
print("status_code: ", r.status_code)
except IOError:
print("File not found: " + tmp101)
print("Have you run setup.sh?")
except:
print("Unexpected error:", sys.exc_info())
-
Prints current conditions.
-
Prints the forecast for the next day.
-
Prints everything returned by the weather site.
Run this by using the following commands:
bone$ ./weather.js Getting weather Temp: 85.1 Humid: 50 Low: 62.02 High: 85.1 sunrise: 2022-07-14 14:32:46
Discussion
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 and app.
-
Be sure to give your app Read and Write permission.
-
Then go to the developer portal and select your 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='4XXX' 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
Run twitter_create_tweet.py_ to see your timeline.
#!/usr/bin/env python
# From: https://github.com/twitterdev/Twitter-API-v2-sample-code/blob/main/Manage-Tweets/create_tweet.py
from requests_oauthlib import OAuth1Session
import os
import json
# In your terminal please set your environment variables by running the following lines of code.
# export 'API_KEY'='<your_consumer_key>'
# export 'API_SECRET_KEY'='<your_consumer_secret>'
consumer_key = os.environ.get("API_KEY")
consumer_secret = os.environ.get("API_SECRET_KEY")
# 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.
payload = {"text": "Hello world!"}
# Get request token
request_token_url = "https://api.twitter.com/oauth/request_token?oauth_callback=oob&x_auth_access_type=write"
oauth = OAuth1Session(consumer_key, client_secret=consumer_secret)
try:
fetch_response = oauth.fetch_request_token(request_token_url)
except ValueError:
print(
"There may have been an issue with the consumer_key or consumer_secret you entered."
)
resource_owner_key = fetch_response.get("oauth_token")
resource_owner_secret = fetch_response.get("oauth_token_secret")
print("Got OAuth token: %s" % resource_owner_key)
# Get authorization
base_authorization_url = "https://api.twitter.com/oauth/authorize"
authorization_url = oauth.authorization_url(base_authorization_url)
print("Please go here and authorize: %s" % authorization_url)
verifier = input("Paste the PIN here: ")
# Get the access token
access_token_url = "https://api.twitter.com/oauth/access_token"
oauth = OAuth1Session(
consumer_key,
client_secret=consumer_secret,
resource_owner_key=resource_owner_key,
resource_owner_secret=resource_owner_secret,
verifier=verifier,
)
oauth_tokens = oauth.fetch_access_token(access_token_url)
access_token = oauth_tokens["oauth_token"]
access_token_secret = oauth_tokens["oauth_token_secret"]
# Make the request
oauth = OAuth1Session(
consumer_key,
client_secret=consumer_secret,
resource_owner_key=access_token,
resource_owner_secret=access_token_secret,
)
# Making the request
response = oauth.post(
"https://api.twitter.com/2/tweets",
json=payload,
)
if response.status_code != 201:
raise Exception(
"Request returned an error: {} {}".format(response.status_code, response.text)
)
print("Response code: {}".format(response.status_code))
# Saving the response as JSON
json_response = response.json()
print(json.dumps(json_response, indent=4, sort_keys=True))
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.
#!/usr/bin/env python
# From: https://github.com/twitterdev/Twitter-API-v2-sample-code/blob/main/Manage-Tweets/delete_tweet.py
from requests_oauthlib import OAuth1Session
import os
import json
# In your terminal please set your environment variables by running the following lines of code.
# export 'API_KEY'='<your_consumer_key>'
# export 'API_SECRET_KEY'='<your_consumer_secret>'
consumer_key = os.environ.get("API_KEY")
consumer_secret = os.environ.get("API_SECRET_KEY")
# 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
id = "1547963178700533760"
# Get request token
request_token_url = "https://api.twitter.com/oauth/request_token?oauth_callback=oob&x_auth_access_type=write"
oauth = OAuth1Session(consumer_key, client_secret=consumer_secret)
try:
fetch_response = oauth.fetch_request_token(request_token_url)
except ValueError:
print(
"There may have been an issue with the consumer_key or consumer_secret you entered."
)
resource_owner_key = fetch_response.get("oauth_token")
resource_owner_secret = fetch_response.get("oauth_token_secret")
print("Got OAuth token: %s" % resource_owner_key)
# Get authorization
base_authorization_url = "https://api.twitter.com/oauth/authorize"
authorization_url = oauth.authorization_url(base_authorization_url)
print("Please go here and authorize: %s" % authorization_url)
verifier = input("Paste the PIN here: ")
# Get the access token
access_token_url = "https://api.twitter.com/oauth/access_token"
oauth = OAuth1Session(
consumer_key,
client_secret=consumer_secret,
resource_owner_key=resource_owner_key,
resource_owner_secret=resource_owner_secret,
verifier=verifier,
)
oauth_tokens = oauth.fetch_access_token(access_token_url)
access_token = oauth_tokens["oauth_token"]
access_token_secret = oauth_tokens["oauth_token_secret"]
# Make the request
oauth = OAuth1Session(
consumer_key,
client_secret=consumer_secret,
resource_owner_key=access_token,
resource_owner_secret=access_token_secret,
)
# Making the request
response = oauth.delete("https://api.twitter.com/2/tweets/{}".format(id))
if response.status_code != 200:
raise Exception(
"Request returned an error: {} {}".format(response.status_code, response.text)
)
print("Response code: {}".format(response.status_code))
# Saving the response as JSON
json_response = response.json()
print(json_response)
To see many other examples, go to iStrategyLabs' node-twitter GitHub page.
Discussion
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 your 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.
Installing Node-RED
Node-RED is already installed and listening on port 1880. Point your browser to http://192.168.7.2:1880, and you will see the screen shown in The Node-RED interface.
Note
|
If NODE-RED doesn’t appear, run the following in a terminal widow. bone$ sudo systemctl start nodered.service Wait a bit, then refresh the browser window running NODE-RED. |
![node-red](figures/node-red.png)
Building a Node-RED Flow - Blink an LED
The example in this recipe builds a Node-RED flow that turn on and off an LED in response to button pushes. First drag the inject node to the canvas and double-click on it.
![node-red-inject](figures/node-red-inject.png)
Set the msg.payload to number and enter 1. Click Done. Make a second inject node, but make it 0.
![node-red-inject2](figures/node-red-inject2.png)
Next scroll down on the left list of nodes to Johnny5 and drag the gpio out node to the canvas and double-click to open it.
![node-red-gpio](figures/node-red-gpio.png)
Set the properties as shown, using the Pin that you’ve wired up. Click Done and wire as shown.
![node-red-wiring](figures/node-red-wiring.png)
Once wired, click the big red Deploy button. Click the 1 inject button and the LED will light up. Click the 0 to turn it off.
Congratulations, your first NODE-RED flow is working.
Installing more nodes
NODE-RED has many other flows and nodes that can be installed. You can see them https://flows.nodered.org/. Here we’ll install twitter. First open Manage palette.
![node-red-wiring](figures/node-red-manage.png)
Note
|
If Manage palette doesn’t appear. Run the following in a terminal widow. bone$ sudo apt install npm bone$ sudo systemctl restart nodered.service Wait a bit, then refresh the browser window running NODE-RED. |
Once Manage palette is open, click on the Install tab and search for twitter. Click the Install button on the first response. Wait a bit.
![node-red-install-twitter](figures/node-red-install-twitter.png)
Click Close once it’s installed.
Building a Node-RED Flow - Twitter
The example in this recipe builds a Node-RED flow that will blink an LED whenever a certain hashtag is tweeted. But first, you need to set up the Node-RED flow with the twitter node:
-
In Node-RED, scroll down until you see the social nodes on the left side of the page.
-
Drag the twitter in node to the canvas, as shown in Node-RED twitter node.
![node-red](figures/node-twitter.png)
-
Authorize Twitter by double-clicking the twitter node. You’ll see the screen shown in Node-RED Twitter authorization, step 1.
![node-red authentication](figures/node-twitter-auth.png)
-
Click the pencil button to bring up the dialog box shown in Node-RED twitter authorization, step 2.
![node-red authentication2](figures/node-twitter-auth2.png)
-
Click on the link, http://developer.twitter.com/en/apps shown in Node-RED twitter authorization, step 2, and follow the steps to set up your twitter app. Copy your API key etc. from there.
-
When you’re back to Node-RED, click the Add button, add your Twitter credentials, enter the hashtags to respond to (Node-RED adding the #BeagleBone hashtag), and then click the Done button.
![node-red beagle hash](figures/node-twitter-beagle.png)
-
Go back to the left panel, scroll up to the top, and then drag the debug node to the canvas.
-
Connect the two nodes by clicking and dragging (Node-RED Twitter adding debug node and connecting).
![node-red debug](figures/node-twitter-debug.png)
-
In the right panel, in the upper-right corner, click the "debug" tab.
-
Finally, click the Deploy button.
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 an LED to response to tweets:
-
Wire up an LED as shown in [displays_externalLED]. Mine is wired to P9_14.
-
Scroll down the left panel and drag the trigger node to the canvas and wire it (Node-RED adding trigger node).
![node-red trigger node](figures/node-disc-out.png)
-
Double-click the trigger node and set the time to 1000 ms.
![node-red trigger setup](figures/node-disc-out-setup.png)
-
Click Done and then Deploy.
Test again. The LED will turn one for 1 second 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.