Have you ever had that feeling of not remembering if you locked the front door? Or perhaps caught up in the rush of the situation, you were locked out without the house keys.
With this blogpost we will show a way to avoid these unpleasant situations through the use of a DISCO-L475VG-IOT01A with arm Mbed OS, AWS IoT Core and the Twitter Direct Messages API.
Assembling the sensorFirst of all, we need to wire the magnetic switch to the STM Board. All you need to do is connect a wire to the GND pin and the other to a digital pin like D1.
We also need to connect the RFID reader to the board. Since our board has Arduino headers, connect the sensor to them as shown in the picture below.
1) Create an account on arm Mbed OS and click on compiler button.
2) Download from our repo the detector project and extract it to a folder of your choice, then from Mbed compiler click on import in order to upload the extracted folder.
AWS IoT Core Setup1) Get to Iot Core
2) Create a policy
3) Then Click Advanced Mode, replace the text field with the texts below. Then click the Create button.
4) Add an IoT device
5) Click Create a single thing button.
6) Input Name anything you like. Then click Next button at the right bottom corner.
7) Click Create certificate button.
Download these 3 files:
- A certificate for this thing (*.cert.pem)
- A private key (*.private.key)
- Root CA for AWS IoT (Save as rootCA.pem) - Choose RSA 2048 bit key: Amazon Root CA 1
Save them in your local machine. Don't share the private key file (*.private.key) with anyone. In addition, don't forget to click Activate button to enable this certificate. Then, click Attach a policy button.
Parameters that need to be set are in MQTT_server_setting.h
1) Replace the content of MQTT_SERVER_HOST_NAME with the Endpoint taken from AWS IoT. You can find your endpoint from Settings in the left menu.
2) Replace the content of SSL_CA_PEM with the content in rootCA.pem downloaded above. You need to add " at the head of each line and \n" at the end of each line. Your SSL_CA_PEM become look like this:
3) Replace the content of SSL_CLIENT_CERT_PEM with the content in *.cert.pem download above. You need to add " at the head of each line and \n" at the end of each line. The content in the file should start with -----BEGIN CERTIFICATE----- and end with -----END CERTIFICATE-----
4) Replace the content of SSL_CLIENT_PRIVATE_KEY_PEM with the content in *.private.pem download above. You need to add " at the head of each line and \n" at the end of each line. The content in the file should start with -----BEGIN RSA PRIVATE KEY----- and end with -----END RSA PRIVATE KEY-----.
Do not share the private key information with anyone.
NOTE
AWS IoT Core doesn't care about the contents of MQTT_CLIENT_ID, MQTT_USERNAME, and MQTT_PASSWORD. You can set any string follows MQTT naming convention.
5) You can set a topic name for publish and subscribe. MQTT_TOPIC_PUB is for publishing and MQTT_TOPIC_SUB is for subscribing. You can set any name follows MQTT naming convention.
6) Setup Wi-Fi, set the security mode and SSID and Password in mbed_app.json.
1) Begin with adding needded import statements and declaring necessary objects into main: the network interface, MQTT network and MQTT client.
//********OUTSIDE MAIN************//
#include "mbed.h"
#include "NTPClient.h"
#include "MQTTNetwork.h"
#include "MQTTmbed.h"
#include "MQTTClient.h"
#include "MQTT_server_setting.h"
#include "mbed_events.h"
#include "mbedtls/error.h"
//Serial out for debug
Serial pc(SERIAL_TX, SERIAL_RX);
//********************************//
//INTO MAIN
NetworkInterface* network = NULL;
MQTTNetwork* mqttNetwork = NULL;
MQTT::Client<MQTTNetwork, Countdown>* mqttClient = NULL;
2) Now we're gonna open the network Wi-Fi interface and connect to an access point.
pc.printf("Opening network interface...\r\n");
{
network = NetworkInterface::get_default_instance();
if (!network) {
pc.printf("Error! No network inteface found.\r\n");
return -1;
}
pc.printf("Connecting to network\r\n");
nsapi_size_or_error_t ret = network->connect();
if (ret) {
pc.printf("Unable to connect! returned %d\r\n", ret);
return -1;
}
}
pc.printf("Network interface opened successfully.\r\n");
pc.printf("\r\n");
3) Sync time with a NTP server
// sync the real time clock (RTC)
NTPClient ntp(network);
ntp.set_server("time.google.com", 123);
time_t now = ntp.get_timestamp();
set_time(now);
pc.printf("Time is now %s", ctime(&now));
4) Connect the board to AWS service endpoint
pc.printf("Connecting to host %s:%d\r\n", MQTT_SERVER_HOST_NAME, MQTT_SERVER_POrT);
{
mqttNetwork = new MQTTNetwork(network);
int rc = mqttNetwork->connect(MQTT_SERVER_HOST_NAME, MQTT_SERVER_PORT, SSL_CA_PEM,
SSL_CLIENT_CERT_PEM, SSL_CLIENT_PRIVATE_KEY_PEM);
if (rc != MQTT::SUCCESS){
const int MAX_TLS_ERROR_CODE = -0x1000;
// Network error
if((MAX_TLS_ERROR_CODE < rc) && (rc < 0)) {
pc.printf("ERROR from MQTTNetwork connect is %d.", rc);
}
// TLS error - mbedTLS error codes starts from -0x1000 to -0x8000.
if(rc <= MAX_TLS_ERROR_CODE) {
const int buf_size = 256;
char *buf = new char[buf_size];
mbedtls_strerror(rc, buf, buf_size);
pc.printf("TLS ERROR (%d) : %s\r\n", rc, buf);
}
return -1;
}
}
pc.printf("Connection established.\r\n");
pc.printf("\r\n");
5) Send the MQTT init packet
pc.printf("MQTT client is trying to connect the server ...\r\n");
{
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
data.MQTTVersion = 3;
data.clientID.cstring = (char *)MQTT_CLIENT_ID;
data.username.cstring = (char *)MQTT_USERNAME;
data.password.cstring = (char *)MQTT_PASSWORD;
mqttClient = new MQTT::Client<MQTTNetwork, Countdown>(*mqttNetwork);
int rc = mqttClient->connect(data);
if (rc != MQTT::SUCCESS) {
pc.printf("ERROR: rc from MQTT connect is %d\r\n", rc);
return -1;
}
}
pc.printf("Client connected.\r\n");
pc.printf("\r\n");
Magnetic sensor and RFIDNow we will show how to check for door state and how to turn on board’s led as an alert emulation (in a production scenario we would ideally play a sound instead) using any of the digital pins and leds of the board. In the example digital pin D1 is used.
1) First things first, pin initialization of the two sensors
//*******************OUTSIDE MAIN***************//
#include "MFRC522.h"
//Pin for MFRC522 reset (pick another D pin if you need D5)
#define MF_RESET D5
//Construct MFRC Object
MFRC522 RfChip (SPI_MOSI, SPI_MISO, SPI_SCK, SPI_CS, MF_RESET);
//***********************************************//
//INSIDE MAIN
//INIT PINs and LED
DigitalIn doorSensor(D1);
DigitalOut led(LED2);
led = 0;
//Check on magnetic sensor
if(doorSensor.is_connected()) {
pc.printf("Door sensor pin is connected and initialized! \r\n");
}
//Set magnetic sensor in PullUp mode
doorSensor.mode(PullUp);
pc.printf("Pull up mode setted\r\n");
//Init the MFRC RFID
RfChip.PCD_Init();
D1 must be set up in PullUp mode, when we will read the pin, “1” will be returned when the circuit is open since it’s used internal resistance of the board in pull up way. It means that the corresponding analog value read is around 3.3 V. On the other hand when the circuit is closed, it will return “0” meaning that it measured 0 V.
2) Let’s go with the main logic
Let’s declare a loop, we will put all of our logic there. The first thing to do is to check if the board is currently connected to our MQTT endpoint (AWS IoT Core), if it’s not, the cycle is broken and cleanup operations are performed. In that case the board needs to be restarted.
while(1) {
/* Check connection */
if(!mqttClient->isConnected()){
break;
}
/* Pass control to other thread. */
if(mqttClient->yield() != MQTT::SUCCESS) {
break;
}
3) Outside the loop, right after it, write the cleanup operations
pc.printf("The client has disconnected.\r\n");
if(mqttClient) {
if(isSubscribed) {
mqttClient->unsubscribe(MQTT_TOPIC_SUB);
mqttClient->setMessageHandler(MQTT_TOPIC_SUB, 0);
}
if(mqttClient->isConnected())
mqttClient->disconnect();
delete mqttClient;
}
if(mqttNetwork) {
mqttNetwork->disconnect();
delete mqttNetwork;
}
if(network) {
network->disconnect();
// network is not created by new.
4) Let’s go back inside our main loop. We will write our “If” statement where we check if the door is open through a digital read on D1 pin. If it is, ID and payload of the message are prepared and our sound alert is turned on (led in our case).
// IF DOOR IS OPEN
if (doorSensor.read() == 1) {
static unsigned short id = 0;
char payload[] = "{ \"payload\": TWITTER-ID-GOES-HERE }";
//START LED ALERT (A SOUND ALERT COULD BE IMPLEMENTED AS WELL)
led = 1;
5) Now we’re coming to the real deal: declare a MQTT message object, setup flags, attach the ID and the payload. Dup flag is false, this means that’s the first time we are sending that message. Retained flag is false, broker doesn’t need to store the packet. QoS is setted to 0 level, it means fire and forget policy.
Then with snprintf() we write the payload buffer into MQTT payload field and also the return value is written in payloadLen field of the MQTT object.
MQTT::Message message;
message.retained = false;
message.dup = false;
const size_t buf_size = 100;
char *buf = new char[buf_size];
message.payload = (void*)buf;
message.qos = MQTT::QOS0;
message.id = id++;
int ret = snprintf(buf, buf_size, "%s", payload);
if(ret < 0) {
pc.printf("ERROR: snprintf() returns %d.", ret);
continue;
}
message.payloadlen = ret;
6) Ok, we started the alert, we prepared the message, let’s send it!
// Publish a message.
pc.printf("Publishing message.\r\n");
int rc = mqttClient->publish(MQTT_TOPIC_SUB, message);
if(rc != MQTT::SUCCESS) {
pc.printf("ERROR: rc from MQTT publish is %d\r\n", rc);
}
pc.printf("Message published.\r\n");
delete[] buf;
7) After the MQTT message has been sent, we need the RFID reader to wait for the FOB to be put on it in order to turn off the alert (Led).
Inside the while loop, right after the code where the MQTT message is published, let's add this code:
//check RFID for shutting off the alert
pc.printf("Wait alert to stop\r\n");
while(! RfChip.PICC_IsNewCardPresent()) {
wait(0.5);
}
pc.printf("Alert stopped\r\n");
//reset led to off state
led = 0;
//Wait until door is closed before restarting alarm, once it's shutted off
//the main cycle can restart checking door sensor again.
pc.printf("Wait door to be closed again\r\n");
while(doorSensor.read() == 1) {
wait(0.5);
}
This way, the program waits until the user puts the FOB near the reader, after which it turns off the alert LED and waits for the door to be closed again before restarting the whole loop.
AWS Lambda and AWS IoT for MEmento projectAs notifier for our project, we choose to use Twitter “Direct Message”. Basically, when the the sensor detect that the user is leaving his home, a Twitter direct message is sent to his personal Twitter account to remember him to lock the door. In this way, the user does not forget to close his home’s door and also remember to keep is key before leaving,
To do this we choose to use two services provided by Amazon (Amazon AWS): AWS Lambda and AWS IoT as the trigger of our Lambda function.
AWS Lambda1) First of all we had to register to Amazon AWS using our Amazon Account. In this way we could have the access to the AWS services using the AWS Management Console
2) We had to create our Lambda function: this is the function that will run every time the user is leaving home (using a trigger that we will explain soon).To do this we have to select “Lambda” in the AWS Management Console in the “Compute” section, and then click on “Create function”.
3) At this point we created a new Lambda function from scratch and we called it “meMento”, and we selected Python 3.7 as Runtime environment. Then we created the function
4) We compressed all the dependencies needed to use the “TwitterAPI” library for Python in a zip file and uploaded it selecting “Upload a.zip file” under the voice “Code entry type”
5) At this point we just have to create our “lambda_function.py” file selecting “Edit code inline” and write our “lambda_handler(event, context)” function (this is the function that will run every time the user open the door).
This function uses the functions provided “TwitterAPI” library to send a direct message from our project’s Twitter account (more on that later) to the user’s Twitter account (identified by its unique account ID number).
In order to send a trigger to the function on AWS Lambda that we will create shortly, we have to go back to the AWS IoT Core console, select our IoT product and create a new rule.
1) To do this, first we have to open the "Act" section of AWS IoT, select "Rules" and then "Create".
2) Now we choose "meMento_IoT_Rule" as the name of our rule and we insert the following SQL line in the "Rule query statement" section:
SELECT * FROM "my/lambda/topic"
3) We click on "Add action".
4) We choose "Send a message to a Lambda function" and then we click on the "Configure action" button.
5) Now we just have to choose the Lambda function called meMento that we created few steps ago and then click on "Add action".
And now our rule is finally configured!
Add our AWS Iot Core Rule as a Lambda triggerTo trigger our Lambda function we just have to add a trigger. To do this we had to select “Add trigger” from the “Designer” section
1) And then select “AWS IoT”
2) Now we have to select "Custom IoT rule" as IoT type and choose the rule that we created few steps ago. After the click on the "Add" button the trigger is created and fully working.
3) At this point we can see our trigger in the Lambda console.
The notifier function on AWS Lambda uses the Twitter Developer API. These API offer many useful REST services, and to make the HTTP requests creation process smoother we used the TwitterAPI Python library. In the code you saw before you needed four parameters that you probably didn't know where to get. In order to have those, first of all you need to create the notifier's Twitter account. This account will send the notifications to the users.
Next, head to https://developer.twitter.com, create a developer account, and once you're done hover on the top right panel with your name, and from the dropdown menu select "Apps". Then select "Create an App".
Once you've created the app for our notifier, you'll find the parameters you need to put in the function right under the "keys and tokens" section. Never share these parameters with anyone or they'll be able to use your Twitter account on your behalf!
Comments