Introduction: Using an Analog Stick As an IoT Key Holder
This project is meant for those who want to use a basic analog stick from an Arduino parts kit as a sensor to detect items like car keys or other weighted objects that can dangle from the analog stick. While this project does not aim to give full analog control to the Raspberry Pi, this will be enough to get an introduction to Internet of Things (IoT) devices.
Step 1: Interfacing the Analog Stick With the Arduino
Although this may vary between different models, analog sticks usually have the following:
- +5V - Input for 5 volts
- GND - connected to Ground
- VRx - how far the stick is pushed horizontally, outputs a value ranging from 0 (pushed all the way to the left), and 1023 (pushed all the way to the right)
- VRy - how far the stick is pushed vertically, outputs a value ranging from 0 (pushed all the way down), and 1023 (pushed all the way up)
- SW - the little button switch that sends a signal when the stick is depressed
For this tutorial, we will have +5V and GND connected to the 5V power and Ground on the Arduino, and have VRx and VRy connected to pin A0 and A1, respectively. This tutorial will not be using the switch. We also have a wire going from Digital Pin 10 on the Arduino to GPIO pin 12 on the Raspberry Pi. Ideally, one would want to use a GPIO header for the Raspberry Pi.
On the Arduino, we want to have a small piece of code running that reads the analog values from the stick, and sends a High signal when the stick reaches a certain threshold. In main.ino, we want it to write a HIGH signal on Digital Pin 10 if the stick is pushed 64 units from the center of the stick. If not, we write a LOW signal. This "deadzone" is meant to reduce false positives, since the stick can be fairly sensitive. Compile and upload the code to the Arduino using the Arduino code editor, or whatever you prefer to use to upload code to the Arduino.
// main.ino
void setup() { Serial.begin(9600); pinMode(10, OUTPUT); }
void loop() { // read values from the analog stick int x = analogRead(A0); int y = analogRead(A1);
// send a HIGH signal if the stick is 64 units (around 6%) from the center if (x > 576 || x < 448){ digitalWrite(10, HIGH); } if (y > 576 || y < 448){ digitalWrite(10, HIGH); } // else, send a low signal else{ digitalWrite(10, LOW); } delay(1000); }
Step 2: Setting Up the Raspberry Pi
As mentioned previously, there is a wire attached to GPIO Pin 12 on the Raspberry Pi. On more information on how to hook up wires to the GPIO pins on your Raspberry Pi, see this Instructable. Note that this could vary depending on which model you have.
This tutorial uses Raspbian on the Raspberry Pi. Other Linux distributions may work, as long as it runs Python.
This tutorial also assumes that the Raspberry Pi is connected to the internet. Please connect it to the internet and get the IP address with ifconfig before continuing as well.
Before we continue, we have to install the following dependencies on our Pi. In the command line type in the line pip install flask flask-wtf flask-restful json requests and press Enter.
Somewhere in our Raspberry Pi, we want to add some files and folders for this project. In your project folder, create the following files and folders:
/project iotapp.py gpio_submit.py /appfolder __init__.py routes.py /templates index.html
Step 3: Creating the Flask Backend
Now that we have created all of these files, let's add some code and get it working.
Starting with routes.py, we'll begin getting the big part of the backend done:
from appfolder import appFlask
from flask import render_template, redirectdata = {'timestamp':['test_date'], 'keys':[True]}
@appFlask.route('/') @appFlask.route('/index') def index(): print(data['timestamp'][-1], data['keys'][-1]) return render_template('index.html', title='Home', dict=data, keys=data['keys'][-1])
We have data as a dictionary in order to store information. More on this later.
In the following lines, we define what would happens when someone wants to visit the site that's being hosted on the Pi, with the format of either http://raspberry pi ip:5000/ or http://raspberry pi ip:5000/index . This directs the user to the main homepage. using render_template, we supply the arguments user and keys, which will be used in the template as a variable.
Now, on to our template index.html
<html>
<head>
<title>Cool IoT template</title>
</head>
<body>
<h1>Is the keyholder occupied?</h1>
<p>
{% if keys == True %}
Yes.
{% else %}
No.
{% endif %}
</p>
<p>
Latest Change: {{ dict.timestamp[-1] }}
</p>
</body>
</html>
Within our first <p> tag we have a conditional statement. We check the value of keys and display a message depending on whether or not the statement is true. These conditionals are enclosed with {% and %}.
Within our second <p> tag we reference another variable. If we want to take a value from the dictionary we have as an argument, we can just reference keys with dictionary.key. Indexing lists also works as it normally does with Python. With dict.timestamp[-1], we're looking at the last object in the list for the key timestamp in the dictionary dict.
Step 4: Setting Up the REST API on the Backend
In order for our app to find out when there are keys present, we run a simple python script called gpio_submit.py in the background. When it detects a change, it sends a POST request to the Flask server on the Pi. Right now, in routes.py, we don't have this behavior, so we're going to add some lines in this file:
from flask_restful import Resource, Api, reqparse, abort
api = Api(appFlask)parser = reqparse.RequestParser() parser.add_argument('timestamp') parser.add_argument('keys')
def str_to_bool(str): if str.lower() == 'true': return True else: return False
class Submit(Resource):
def get(self):
return 404
def post(self): args = parser.parse_args()
if (args['timestamp'] == None) or (args['keys'] == None): return '401 unauthorized', 401 print(args['timestamp'], args['keys'])
data['timestamp'].append(args['timestamp']) data['keys'].append(str_to_bool(args['keys']))
return data['timestamp'], 201
api.add_resource(Submit, '/submit')
There's a lot to take in, so let's look at this step by step:
- We begin by instantiating the api, and supply all of the arguments needed
- We also have a helper function in place to convert a string to a boolean object. It checks to see if the lowercase string is equal to 'true', and returns True if this is the case, else it returns False
- We have a new class called Submit, taking Resource as an argument. This will allow us to let this class work with our Flask server
- In this class, we have a function for post. This will check to see if both of the arguments are present, and if it is it will add the values to the dictionary we established before
- At the last line, we use the class and
So, in the end, when we do a POST request on http://raspberry pi ip:5000/submit with the appropriate arguments, it will add the data to the dictionary.
Step 5: Creating a Helper Tool to Send Information to the Flask Server
Now that we have that together, we want the Raspberry Pi to look at the signal on GPIO pin 12, and send a POST request to the server when the signal changes. In gpio_submit.py, add the following:
import RPi.GPIO as GPIO
from time import sleep import json import requestsfrom datetime import datetime
uri = 'http://127.0.0.1:5000/submit'
GPIO.setmode(GPIO.BCM) GPIO.setup(12, GPIO.IN)
This contains all of our initital items, including imports, the url for the server, and setting up the GPIO pins to receive an input on pin 12. Since this is most likely running on the Raspberry Pi with the Flask server, we use the IP address 127.0.0.1 as this is the IP address for anything local. If the Flask server is somewhere else, change the IP address to the IP for the Flask server.
def submit_data(uri, keys):
data = {'timestamp':datetime.utcnow(), 'keys':str(keys)} r = requests.post(uri, data=data)
This is a helper function we created in order to post data to the server. This will be used whenever there's a change in the signal we receive on GPIO pin 12
keys = GPIO.input(12)
if keys == True: submit_data(uri, True) else: submit_data(uri, False)
After we have everything established, we run this bit of code. This will initialize keys to whatever value we are receiving on pin 12, and send that information to the server.
try: while(1): print(GPIO.input(12)) if (keys == True): if GPIO.input(12) == False: print("keys no longer present") submit_data(uri, False) keys = False else: if GPIO.input(12) == True: print("keys present") submit_data(uri, True) keys = True sleep(5) except KeyboardInterrupt: GPIO.cleanup()
Over here we have an infinite while loop running within a try statement. This will continue running until there's a keyboard interrupt, as in you press ctrl+c while this is running. In this while loop, this will check pin 12 to see if there has been a change since the last iteration of the while loop. If this is the case, it sends this information to the Flask server, and updates the value of keys. In the end of this while loop, it sleeps for 5 seconds. This can be easily changed, or even removed entirely.
Step 6: Running the Code
Now that we have everything established, we will run the code.
For our flask app, we will have to do something first. In the directory of iotapp.py, type and execute the following:
export FLASKAPP=iotapp.py
This will set the file for our Flask web application when it runs.
Now, type in python -m flask run --host=0.0.0.0. This will run the Flask app, and by setting the host to 0.0.0.0, this will allow other devices on the same network to access the Pi if they have the IP address for the Pi.
In a separate terminal window, ssh connection, or screen, go to the directory for gpio_submit.py and type in and execute python gpio_submit.py.
Using a device on the same network, connect to the IP on the Raspberry Pi on port 5000 and see your fruits of labor. Place your keys or some sort of weight on the analog stick, refresh the page, and confirm that everything's working. Position the components in a desirable way, and then have fun with your new keyholder.