---
title: 'Project IoT'
disqus: hackmd
---
## Tutorial on how to build an ultrasonic alarm system
**Author**: Viking Forsman
**Student ID**: im222fz
**Approximate time**: 3 hours
In this tutorial I will provide instructions on how to build an alarm system that is triggered via ultrasonic distance measuring. The basic idea behind this alarm system is that the alarm device is placed with an unobstructed view to a door. The user activates the system with a button press which initiates a countdown before the alarm is activated, which allows the user to leave and close the door. The system then performs periodic measurement of the distance to a door. A second countdown is triggered if the measured distance is deviating more than a specified threshold from the initial measurement. The system assumes that the person is an intruder if he or she does not press the button before the countdown finishes. If the countdown reaches its endpoint the system will start the emitting sounds with a passive buzzer and send a message to a REST API which in turn will trigger an SMS containing a warning to owner’s phone.
The required time to perform all steps in the tutorial is approximately three hours, but this may vary depending on how experienced the developer is with programming, electrical engineering and IoT.
### Objective
I choose this project because it is simple enough to be an introductory project while still providing plenty of opportunities for more experienced developers to expand the functionality the system. The finished system could serve as a deterrent from nosy family members or perhaps as a complementary home alarm system. The tutorial can provide some basic insight in how to connect the pycom device to sensors and actuators and how to push sensed data to an IoT platform. The data that is pushed to the platform is the alarm's status (offline, online, triggered, Unauthorized entry).
### Material
I got all the sensors and actuators in this project from a [bundle](https://www.kjell.com/se/produkter/el-verktyg/arduino/moduler/playknowlogy-stora-modul-paketet-for-arduino-p87291) sold by kjell & company, this bundle cost 499 SEK and it includes thirty-eight different modules. I acquired the LoPy 4, Expansion Board 3.0, breadboard and wires from [another bundle](https://www.electrokit.com/produkt/lnu-1dt305-tillampad-iot-lopy4-and-sensors-bundle/) which is sold by the company electrokit for 995 SEK. Lastly a [battery holder](https://www.electrokit.com/produkt/batterihallare-3xaaa-med-strombrytare-och-jst-kontakt) is necessary if the device should be runnable when not connected to a computer, the one I bought cost 29 SEK. Combined the total price for all material is 1523 SEK. However, for this tutorial not all components in the bundles are necessary and the required components can be purchased separately from several different vendors at a lower cost.
| Component | Usage | Image |
| -------- | -------- | -------- |
| Expansion Board 3.0 | Device to execute the code on |  |
| LoPy 4 | Device to execute the code on |  |
| Breadboard | Connects the components and pycom device |  |
| Wires | Connects the components and pycom device |  |
| Dual ultrasonic sensor | Measure distance in ranges from 2 cm to 400 cm with a resolution of 0.3 cm |  |
| Button switch module | Button that can provide user input to the device |  |
| Passive buzzer module | Emit a range of sounds depending on the input frequency |  |
| Battery Holder | Provide electricity to the device (output voltage of 3.3 – 4.5V) |  |
### Computer setup
The chosen IDE for this project is [Atom](https://atom.io/) which is a free and open source IDE that supports customizable commands and plugins. It is also one of the two IDE:s which supports the [pymakr](https://atom.io/packages/pymakr) plugin. I used this plugin since it provides an easy way to upload project files or directly execute commands on the pycom device. Atom is available for macOS 10.9 or later, Windows 7 and later, and Linux (RedHat or Ubuntu).
The official pycom ["getting started guide"](https://docs.pycom.io/gettingstarted/) recommends that the expansion board's firmware should be updated before using the hardware. The pycom device will work out of the box for Windows 8 or later (I used Windows 10), otherwise additional drivers need to be installed. The firmware is continuously updated, and the most recent version is often the most stable version. So, it is a good idea to keep the firmware updated. There is a [tool](https://software.pycom.io/findupgrade?product=pycom-firmware-updater&type=all&platform=win32&redirect=true) that allows the user to choose which version of the firmware to use.
### Putting everything together
The image below depict how the sensors and actuators are connected to the pycom device.

### Platform
The IoT platform I have chosen is [Ubidots](https://www.ubidots.com/) which enable users to send data to the cloud from internet-enabled devices. Ubidots can visualize the received data in dashboards that are customizable with HTML code and JavaScripts. Finally, Ubidots also allows the user to assign certain events to occur depending on the received data. These events include sending messages via SMS, email, slack or WebHocks.
Ubidots has a free version for educational or personal use but it has some limitations regarding the amount of active devices, messages and stored data. For this project the free version is more than sufficient, since we only need one active device and relatively few messages.
Ubidots expects to receive messages with at most three fields: a value, a context, and a timestamp. The value part of the message is mandatory while the context and timestamp is optional. The value must be either an integer or float, nonnumerical values are not supported. However, nonnumerical information can be included in the optional context part of the message. The timestamp can either be assigned by the device with Unix epoch time (in milliseconds) or this value will be assigned by the server when it is received.
### The code
The code of the system is displayed in the listing below. The implementation is written in MicroPython which is a lean subset of python that is optimized for running on microcontrollers. I have tried to make the code easy to understand by including plenty of comments and print messages. The lopy LED light is used to indicate different states of the system. Green indicates that the alarm is offline and is waiting for activation, red indicate that the system is active, blue indicate that a timer has been initiated (either from starting the system or a threshold has been reached).
There is not enough room in the tutorial to describe all the functions in depth, but I will provide a brief description of the three main functions. The function **alarm_activation()** start a loop that exits when the button is pressed. The function **alarm_execute()** measure an initial distance and then start a loop in which the distance is remeasured. If the distance deviates more than 5 cm from the initial value or if the button is pressed the function returns a boolean value. If this boolean is true the **alarm_triggered()** function will be called, which starts a timer. If the button is pressed before the timer runs out the function exit, otherwise the passive buzzer will be actuated to emit sounds. The complete implementation can be found in this [GitHub repository](https://github.com/GunGoat/1DT305-project-final).
```python=
from network import WLAN # Used for connecting the device to Wifi
import urequests as requests # Used for sending data to ubidots
from machine import Pin # Used for defining the sensor and actuator pins
import pycom # Used for the LED lights
import time # Used for measuring time in seconds
import utime # Used for measuring time in microseconds
# Pins
button = Pin('P9', mode=Pin.IN, pull=Pin.PULL_UP)
trig = Pin('P10', mode=Pin.OUT)
echo = Pin('P11', mode=Pin.IN)
sound = Pin('P12', mode = Pin.OUT)
time.sleep(2)
# Wifi
wlan = WLAN(mode=WLAN.STA)
wlan.antenna(WLAN.INT_ANT)
wlan.connect("Name here", auth=(WLAN.WPA2, "Password here"), timeout=5000)
while not wlan.isconnected ():
machine.idle()
print("Connected to Wifi\n")
token = "Token here" # Ubidots token
# Variables
red = 0x7f0000
green = 0x007f00
blue = 0x00007f
# Function that builds the json
def build_json(variable, value):
try:
message = {1: "Alarm is offline", 2: "Alarm is online", 3: "Alarm is triggered", 4: "Unauthorized entry"}
data = {variable: {"value": value, "context" : {"message" : message[value]}}}
return data
except:
return None
# Function that sends the request to the REST API https://ubidots.com/docs/api/
def post_var(device, value):
try:
url = "https://industrial.api.ubidots.com/api/v1.6/devices/" + device
headers = {"X-Auth-Token": token, "Content-Type": "application/json"}
data = build_json("status", value)
if data is not None:
print(data)
req = requests.post(url=url, headers=headers, json=data)
return req.json()
else:
print("Error, Invalid data!")
pass
except:
print("Error, invalid request!")
pass
# Function that measure the distance
def measure_distance():
# Calculate the offset (may change depending on hardware)
start = utime.ticks_us()
end = utime.ticks_us()
offset = utime.ticks_diff(end, start)
# Emit ultrasonic sound for 10 microseconds
trig.value(1)
utime.sleep_us(10)
trig.value(0)
# Calculate the difference in time between the sent signal and the echo
start = utime.ticks_us()
while not echo():
pass
end = utime.ticks_us()
difference = utime.ticks_diff(end, start) # measuring travel time
# Return the distance in centimeters
return ((difference-offset)/2)/29
# Function that active the alarm via a button press
def alarm_activation():
print("Press the button to activate the alarm...")
post_var("pycom", 1)
pycom.rgbled(green)
while True:
if button() == 0:
print("The alarm will be active in 20 seconds!")
pycom.rgbled(blue)
time.sleep(20)
post_var("pycom", 2)
break
time.sleep(1)
# Function that can trigger the alarm depending on the ultrasonic sensor
def alarm_execute():
pycom.rgbled(red)
print("The alarm is now active!")
print("Press the button to deactivate the alarm...")
# Measure the distance initial distance to the door
distance_initial = measure_distance()
time.sleep(1)
# Determine the lower and higher threshold
Distance_higher_threshold = distance_initial + 5;
Distance_lower_threshold = distance_initial - 5;
while True:
distance = measure_distance()
print('Deviation:', distance - distance_initial, 'cm')
utime.sleep(1)
# Determine if thresholds are crossed or if the button is pressed
if button() == 0:
print("The alarm was deactivated with the button...")
pycom.rgbled(green)
time.sleep(1)
return False
elif distance < Distance_lower_threshold:
print("The alarm was triggered the lower threshold...")
post_var("pycom", 3)
return True
elif distance > Distance_higher_threshold:
print("The alarm was triggered by the higher threshold...")
post_var("pycom", 3)
return True
# Function that emit sound from buzzer
def alarm_sound(duration):
t_end = time.time() + duration
while time.time() < t_end:
sound.value(1)
time.sleep(0.002)
sound.value(0)
time.sleep(0.002)
# Function that measure distance and trigger the alarm
def alarm_triggered():
# check for button press in 20 seconds
t_end = time.time() + 20
pycom.rgbled(blue)
while time.time() < t_end:
if button() == 0:
print("The alarm was deactivated with the button...")
pycom.rgbled(green)
time.sleep(1)
return
time.sleep(1)
# sound the alarm for 60 seconds
pycom.rgbled(red)
post_var("pycom", 4)
alarm_sound(60)
# Looping through the main behaviors of the system
pycom.heartbeat(False)
while True:
alarm_activation()
if alarm_execute():
alarm_triggered()
```
### Transmitting the data / connectivity
How often data is transmitted to the internet in this project is hard to determine, since no packages are scheduled in accordance to a time period. Instead the packages are transmitted based on events. The system sends a message when the alarm is activated or deactivated. The system also sends a message when the alarm is triggered, and if the alarm is not deactivated within a specified time frame another message is sent.
Initially, I considered transmitting data via the LoRa protocol since it has long range. Unfortunately, there were no gateways within a reasonable range where I live. Therefore I decided to use WiFi instead which has much more limited range but can send larger messages more frequent. The data is sent to via HTTP to the Ubidots REST API using the urequests module.
### Presenting the data
The data that triggers the events are integers representing the states (offline, online, triggered, Unauthorized entry). The context information are strings that contains the alarm status in written form, which is less cryptic, so that is what I chose to display in the log table. The most recently received status information is presented in the widget on the top. The dot chart on the right visually displays when the messages were received and the system status in integer form, the X-axis represents the time and the Y-axis the status value. It is hard to determine how often data is saved, due to the same reason as in the previous section.

### Finalizing the design

I am quite happy with the outcome with the project, especially since it was my first time working with IoT. I had hoped to be able to try LoRa when connecting the pycome to the internet, since it seems to be prevalent in IoT projects. But the only gateway in my vicinity was 4 km away on ground level inside a building. I had to get really close to get a connection with this gateway, which would have been too impractical during the development process. For my next IoT project I will probably try to use sigfox.
An improvement on the current implementation would be to assign the context information in the server instead of sending this information from the device, since this would reduce the overall size of the messages. This could be done with the HTML Canvas widget in Ubidots, which allows the user to define dynamic behavior based on the received data using JavaScripts.
Another possible improvement would have been to provide a 3d printed case so that it could be safely transpored and used in outdoor environments. This would however require a 3D printer which would significantly increase the budget for the project. Therefor I had to make do with a simple wooden box in this project.
