This post is intended to provide an hands-on step-by-step explanation of how to setup the hardware and the software in order to reproduce my Smart Home Alarm System fully described in the linked GitHub repository.
The alarm system collects information from a Hall effect sensor placed on a door prototype in order to check for possible intrusions. In case of break-in, an alarm is triggered. The MCU is developed using RIOT-OS, while for cloud services I used the AWS ecosystem.
Hardware ConfigurationFrom this post it is possible to retrieve the circuit schematics in order to reproduce the system. I used the pin 2 for the button, the pin 12 for the LED, the pin 23 for the buzzer and the analogic pin 36 for the Hall Effect sensor. For the physical placement of the system, instead, two round magnets must be placed ideally in correspondence of the door handle and the labelled side of the Hall effect sensor must be placed near the magnet surface when the door is closed.
MCU StartupThe MCU will run RIOT-OS code, so I provide the official reference to getting started with this OS. After RIOT installation, these are the steps to follow:
git clone https://github.com/pasqualemocerino/Smart-Home-Alarm-System
cd Smart-Home-Alarm-System/code
BOARD="esp32-heltec-lora32-v2" BUILD_IN_DOCKER=1 DOCKER="sudo docker" PORT=/dev/ttyUSB0 make all flash term
If there are some issues with serial port permissions, simply run:
sudo chmod a+rw /dev/ttyUSB0
And then try again with:
BOARD="esp32-heltec-lora32-v2" BUILD_IN_DOCKER=1 DOCKER="sudo docker" PORT=/dev/ttyUSB0 make all flash term
This last command is used to flash the firmware on the MCU and to start the system. For the moment, it is sufficient to simply try to make it work, because it will be needed when all other configurations will be completed. The first step is MQTT Broker configuration.
Mosquitto MQTTBroker Configuration
The first needed step is to install Mosquitto on your OS. A general reference is the official download page. In my case, I installed it by simply using snap, however it is also possible to use apt.
snap install mosquitto
The next step is to allow local network access, since the Mosquitto MQTT Broker will be running locally. The simplest way is to add the following lines to Mosquitto configuration file etc/mosquitto/mosquitto.conf. So, after accessing to the file with:
cd etc/mosquitto/
sudo nano mosquitto.conf
It is simply needed to add the following lines at the end of the configuration file:
listener 1883
allow_anonymous_true
The port 1883 is the default one. In this way we are configuring unauthenticated access. For more refined authentication methods, it is possible to check the official Mosquitto guide for Authentication methods.
So, now the Mosquitto MQTT Broker is correctly configured to accept local connections. It is possible to check that the Broker is working locally by executing the MQTT subscribe command from one terminal:
mosquitto_sub -h localhost -t /topic
And by executing the MQTT publish command from a different terminal:
mosquitto_pub -h localhost -t /topic -m "Hello"
If the specified message is displayed on the first waiting terminal, the configuration is correct.
Bridge Mosquitto MQTT Broker to AWS IoTThe next phase is to correctly bridge the local Mosquitto MQTT Broker to AWS IoT Core module. The main reference for this procedure is the official AWS guide. Differently from this guide, I skipped the part of EC2 configuration, so I moved directly to the section How to Configure the Bridge to AWS IoT Core.
After installing the AWS CLI on my PC by simply following the official AWS CLI installation guide, I executed some commands, as specified by the main reported guide. However, for this first command:
aws configure
Contrarily to what is specified by the guide, I had to also setup AWS Access and AWS Secret Access Key, apart from the region name. Furthermore, I also had to set AWS session token by running:
sudo aws configure set aws_session_token <aws-session-token-value>
The other commands were substantially the same, so:
aws iot create-policy --policy-name bridgeMQTT --policy-document '{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Action": "iot:*","Resource": "*"}]}'
cd /etc/mosquitto/certs/
sudo wget https://www.amazontrust.com/repository/AmazonRootCA1.pem -O rootCA.pem
sudo aws iot create-keys-and-certificate --set-as-active --certificate-pem-outfile cert.crt --private-key-outfile private.key --public-key-outfile public.key --region us-east-1
aws iot attach-principal-policy --policy-name bridgeMQTT --principal <certificate ARN>
sudo chmod 644 private.key
sudo chmod 644 cert.crt
In this way we have a certificate for the bridge associated with an IoT policy giving permissions to the bridge to connect to AWS IoT Core and publish/subscribe to any topic (the policy can be modulated according to application needs).
The last step is to create the configuration file with our specific configuration for the Mosquitto Broker bridge. After retrieving the value of the AWS IoT Core ATS endpoint with the command:
aws iot describe-endpoint --endpoint-type iot:Data-ATS
The configuration file is created with:
sudo nano /etc/mosquitto/conf.d/bridge.conf
The main content of the file is (by inserting the right AWS IoT Core endpoint):
# ============================================================
# Bridge to AWS IOT
# ============================================================
connection awsiot
#<Paste your AWS IoT Core ATS endpoint retrieved from the AWS CLI in the form of xxxxxxxxxxxxxxx-ats.iot.<region>.amazonaws.com:8883
address xxxxxxxxxxxxxxx-ats.iot.<region>.amazonaws.com:8883
# Specifying which topics are bridged and in what fashion
topic awsiot_to_localgateway in 1
topic localgateway_to_awsiot out 1
topic both_directions both 1
# Setting protocol version explicitly
bridge_protocol_version mqttv311
bridge_insecure false
# Bridge connection name and MQTT client Id, enabling the connection automatically when the broker starts.
cleansession true
clientid bridgeawsiot
start_type automatic
notifications false
log_type all
# ============================================================
# Certificate based SSL/TLS support
# ============================================================
#Path to the rootCA
bridge_cafile /etc/mosquitto/certs/rootCA.pem
# Path to the PEM encoded client certificate
bridge_certfile /etc/mosquitto/certs/cert.crt
# Path to the PEM encoded client private key
bridge_keyfile /etc/mosquitto/certs/private.key
#END of bridge.conf
In my case, I also added the specification of my own defined topics (in the relative section):
topic home/doors/+/system out 1
topic home/doors/+/alarm out 1
To test the configuration, it is possible to execute the following command in order to publish a local message to the Mosquitto MQTT Broker:
mosquitto_pub -h localhost -p 1883 -q 1 -d -t localgateway_to_awsiot -i localClientID -m "{\"message\": \"helloFromLocalGateway\"}"
AWS IoT Core Thing ConfigurationIn this phase we simply need to create a new thing in AWS IoT Core and to obtain the needed certificates and keys to link to the MQTT Python bridge code.
Starting from Connect one devicesection of AWS IoT Core, it is needed to simply follow given instructions. I chose Python option for AWS IoT Device SDK. So, I downloaded and unzipped the connection kit. The certificate and the keys of the device are needed for the correct working of MQTT Python Bridge.
Python MQTT Bridge ConfigurationThe Python MQTT Bridge code simply connects to the local MQTT Broker by specifying the IP address and the port and subscribes to the specified topics. When a message is received, the Bridge publishes the message on AWS IoT Core.
The parameters to set are the following ones:
# MQTT broker
MQTT_BROKER_ADDR = "INSERT THE IP ADDRESS OF LOCAL MQTT BROKER"
MQTT_BROKER_PORT = 1883
MQTT_BROKER_CLIENT_ID = "bridgeawsiot"
# AWS constants
AWS_IOT_ENDPOINT = "INSERT THE AWS IOT ENDPOINT IN THE FORM xxxxxxxxxxxxxx-ats.iot.<region-name>.amazonaws.com"
AWS_IOT_PORT = 8883
AWS_IOT_CLIENT_ID = "basicPubSub"
AWS_IOT_ROOT_CA = "ABSOLUTE PATH FOR root-CA.crt FILE"
AWS_IOT_PRIVATE_KEY = "ABSOLUTE PATH FOR <thing-name>.private.key FILE"
AWS_IOT_CERTIFICATE = "ABSOLUTE PATH FOR <thing-name>.cert.pem FILE"
# Topics of my application, coherent with the ones specified in bridge.conf
MQTT_TOPIC_SYSTEM = "home/doors/1/system"
MQTT_TOPIC_ALARM = "home/doors/1/alarm"
The main code can be obtained from the repository.
To test that the MQTT Bridge correctly works, it is possible to start the Python code in background with the command:
python3 mqtt_bridge.py
And to send some messages to the local MQTT Broker. If everything is correctly configured, the messages will be transmitted to AWS.
AWS DynamoDB ConfigurationThe next step is to store device data in NoSQL DynamoDB tables, in my case I used two tables: SystemTable, to store information about alarm system starts and stops and AlarmTable, to store information about alarm starts and stops, with relative measurements of open door.
This step is relatively easy since it is well documented in the official AWS guide.
I only report relevant information of my own infrastructure. The two DynamoDB tables are SystemTable, both with Partition Key as sample_time (Number) and Sort Key as device_id (Number).
The two AWS IoT Core rules are instead system_data_ddb for SystemTable and alarm_data_ddb for AlarmTable.
For both Partition Key value is set to ${timestamp()} and Sort Key value to ${cast(topic(3) AS DECIMAL)}. Obviously, they differ for the SQL statement. For system_data_ddb it is:
SELECT system,
FROM 'home/doors/+/system'
While for alarm_data_ddb it is:
SELECT alarm, m_field,
FROM 'home/doors/+/alarm'
As specified in the guide, it is possible to test the AWS IoT rule and the correct insertion of data in the DynamoDB tables by simply publishing data in the correct format to related topics. For simplicity, I published data only to home/doors/1/system in the format {\"system\":\"<0 or 1>\"}" and to home/doors/1/alarm in the format {\"alarm\":\"<0 or 1>\", \"m_field\":\"<int-value>\"}". Data are correctly inserted in the tables by also adding the timestamp and by extracting the device ID, in this case 1.
AWS Lambda functionThe next step is to retrieve data from databases and to make it accessible through an API. The official reference is as usual the AWS guide.
However, in my case I created a Lambda function called AlarmMonitorFunction from scratch by selecting Python 3.10 as runtime environment. In the Permissions section, it is important to assure the possibility to access data from the two tables by specifying proper roles. In my case I used an AWS Academy profile, so I simply used the LabRole, which allows to access the tables.
The code of the Lambda function is the following one:
import json
# import the AWS SDK
import boto3
# Funtion to sort by sample time
def sort_by_key(list):
return list['sample_time']
# Define the handler function that the Lambda service will use
def lambda_handler(event, context):
# Create a DynamoDB object using the AWS SDK
dynamodb = boto3.resource('dynamodb')
# Use the DynamoDB object to select tables
system_table = dynamodb.Table('SystemTable')
alarm_table = dynamodb.Table('AlarmTable')
# Retrieve tuples of our tables to return
output_system = system_table.scan()["Items"]
output_alarm = alarm_table.scan()["Items"]
# Sort by sample time
output_system = sorted(output_system, key=sort_by_key);
output_alarm = sorted(output_alarm, key=sort_by_key)
return {
'statusCode': 200,
'body_system': output_system,
'body_alarm': output_alarm
}
This code simply retrieves and gives in output data contained in the two tables, ordered by sample time and associated to the body_system field and body_alarm field. The function can be tested by simply creating an empty test event.
We have not done yet with Lambda Functions, because we need two more functions for next steps.
A first AWS Lambda function will handle POST requests coming from the dashboard. This function will simply take the payload sent when pushing the dashboard button and it will publish it to both_directions topic, bridged with local Python MQTT Bridge script. The script is subscribed to this topic, so, when receiving the payload, it will publish it on the system local Moaquitto MQTT topic, so that the MCU can receive it and activate/deactivate the system with the usual procedure.
import json
import boto3
def lambda_handler(event, context):
client = boto3.client('iot-data', region_name='us-east-1')
response = client.publish(
topic='both_directions',
qos=1,
payload=json.dumps(event)
)
return {
'statusCode': 200
}
The last needed AWS Lambda Function is CognitoAutoConfirm, that will be used to auto confirm user emails during sign up phase, due to an issue with mail receiving from AWS.
import json
def lambda_handler(event, context):
event['response']['autoConfirmUser'] = True
if 'email' in event['request']['userAttributes']:
event['response']['autoVerifyEmail'] = True
if 'phone_number' in event['request']['userAttributes']:
event['response']['autoVerifyPhone'] = True
return event
AWS API GatewayNow it is time to make data accessible from the outside by deploying an API. In the AWS API GatewayConsole, I selected Create API and Build on REST API section. So I left checked the REST and the New API options and I inserted the API name, in my case AlarmMonitor. So I selected Edge optimized as Endpoint Type.
From Actions dropdown, I selected Create Method and then the GET method on the new available dropdown, so I confirmed by clicking on the confirmation button. At this point, I inserted the previously created AlarmMonitorFunction as Lambda Function. So, finally I clicked on Save and then on OK.
Now, we need to insert a POST method for remote activation. From Actions dropdown, I selected Create Method and then the POST method on the new available dropdown, so I confirmed by clicking on the confirmation button. At this point, I inserted the previously created SystemHandlerFunction as Lambda Function. So, finally I clicked on Save and then on OK.
We are not done yet. Now, again from Actions dropdown, I selected the Enable CORS option and then I simply clicked on the Enable CORS and replace existing CORS headers button, and then on Yes, replace existing values one. This step is important because I was not able to access data without enabling CORS. So, we simply need to deploy the API: from Actions, it is needed to select Deploy API option. I selected [New Stage] as Deployment Stage and I chose dev as Stage name (also prod can be used).
After clicking on Deploy, it is possible to obtain an Invoke URL, from which raw JSON data are displayed.
AWS CognitoFirstly, we create a new user pool. From User pools section, I selected Create user pool, then I checked User name and Email in Cognito user pool sign-in option, and Allow users to sign in with a preferred user name and Make user name case sensitive as User name requirements. After selecting Next, I selected Optional MFA with Authenticator apps option checked and I left other options to the default, so I selected Next. I selected given_name in Required attributes, but it is not necessary, the list of attributes can be freely chosen. So, after clicking on Next, I selected the option Send email with Cognito, and then Next again. At this point, I inserted the user pool name, in my case smart-home-alarm-system-pool, so I checked Use the Cognito Hosted UI, so I inserted a domain prefix, in my case smart-home-alarm-system. So I inserted app client name, in my case alarm-app, I inserted http://localhost:5500/website/home.html as allowed callback URL and then I clicked on Next and finally on Create user pool, after revision. In fact, I simply activated the Live Server extension of Visual Studio Code to host the page, but other solutions can be adopted as well,
From the user pool console, I selected User pool properties and Add Lambda trigger, choosing CognitoAutoConfirm as a Pre sign-up trigger, in order to automatically confirm e-mail at sign-up. The last step to do is to specify the URL to redirect to in case of sign-out. From App integration, I selected the previously created app from the given app link and then I chose Edit from Hosted UI section. So, I inserted http://localhost:5500/website/index.html as allowed sign-out URL and on Save changes. As specified before, these specific URLs were used as a simply solution, but it is better to use HTTPS URLs hosted in a different way.
To test that everything is correctly configured, these are the steps to follow. From App integration section, it is needed to access as before to the previously created app from the given app link. So, from View Hosted UI it should be possible to access to the sign-in page. After selected the Sign-up option and registering a new user, it should be possible not only to access the page content, but also to logout and sign-in again. The new user will appear in AWS user pool. To better understand how to avoid website code errors, some functions to check are certainly Javascript function that handles button activation/deactivation of the system, and especially the structure of login/logout href URLs, in the provided HTML code.
So, the authentication system is correctly setup.
AWS AmplifySo, we only miss the deployment of the web app using data from the API. From AWS Amplify Console, I selected New App dropdown menu and then Host web app. It is possible to link a GitHub repository, which is probably a better solution. However, the fastest way to deploy the app is to simply select Deploy without Git provider. After selecting Continue, I inserted the app name, in my case alarm_app and the environment name, so dev. So, I simply created a zip folder containing the web app files (index.html, index.js and style.css, accessible from the GitHub repository), named web.zip, and I dragged and dropped it on the relative section.
Finally, an Amplify App URL to access the web app will be provided. From this URL it is possible to access the dashboard in order to check relevant data about the alarm system.
System testingThe system must be tested after following all the steps of the guide and specifying the broker IP address for the MQTT Bridge and the RIOT-OS code, as well as the Wi-Fi SSID and password in Makefile. In detail, it is needed to execute the following code from a first terminal, to flash the firmware to the connected ESP32 and to start it:
cd Smart-Home-Alarm-System/code/
BOARD="esp32-heltec-lora32-v2" BUILD_IN_DOCKER=1 DOCKER="sudo docker" make all flash term
From another terminal, the Python MQTT Bridge myst be started by executing:
cd Smart-Home-Alarm-System/code/MQTT_Bridge/
python3 mqtt_bridge.py
With closed door, a first test is to simply push the button for a small range of time and check for system activation. Keeping the door closed, it is possible to test system deactivation by pushing again the button. In both cases, an activation/deactivation message is sent to the cloud through the bridge. Another test is to start the system with closed door and to simulate a break-in by opening the door. The alarms activates rapidly and alarm messages are sent to the cloud through the bridge, until the button is not pushed again.
By accessing the web-based dashboard from AWS Amplify app URL (hosted locally with Visual Studio Code Live Server), after sign-up and sign-in it is possible to access the dashboard content (it is important to check carefully HTML embedded URLs and Cognito app redirect URLs for this phase). From the dashboard. it should possible to see collected data and related statistics. The last test is to click on activation button from the dashboard. The prototype alarm system will be remotely activated, and it will be possible to test again the alarm by opening the door. Finally, by clicking on the deactivation button from the dashboard, the alarm system will be remotely deactivated.
ConclusionsI tried to provide a clear guide about the system implementation in a unique blog post. However, if I missed something or some steps are not so clear, I will try to improve this post by adding more details.
Linked GitHub repository constitutes the main reference of the project, with further details about concept, design and evaluation of the system.
Comments