Building a smart (IoT) product is more an art than it is a science because it is about unifying the physical world with the digital one. When you put a piece of hardware on the web, magic happens. But one device on the web is one thing. Think about tens of them, interconnected, forging an automated ecosystem of pure reverie. Now imagine tens of thousands of these ecosystems spread throughout the globe. Seems profound, no? But developing an IoT product and then managing it is just as profoundly difficult. It involves development across a huge technology stack (your IoT hardware itself, your apps to monitor/control your hardware and a backend in between for the magic) to make such products work in production. And then in production comes the hardest challenge; you are going to have to scale it up as more and more devices connect through it and your user base grows.
Therefore making your system event-driven from the start goes a long way and simplifies a lot of things as you scale. But what is an event-driven system?
An event-driven system is highly scalable and resilient to failures as components are loosely coupled and can be scaled independently.
One part of the system does not need to know about other parts and what's out there. An event can be generated at one point of the system and is processed at several other points without the source or sink knowing about any who or hows. This makes it a very powerful choice for distributed architectures.
And in the IoT world where thousands of nodes can be connected via the web, having event-drive in your system quickly becomes crucial. Switching off most of your appliances and regulating others to power-saving mode when you go out on vacation, fire alarm notifying all members of the house about a could-be emergency, theft-detecting sensors enabling your building’s security system and updating the guards about the status — you can’t achieve these use cases efficiently, if at all, solely by polling.
And that’s why Grandeur is designed as an event-driven architecture (EDA). Devices can listen for updates from their users, users can listen for updates from their devices, users can publish updates to their devices which the devices can react to instantly, and devices can publish updates which the users can make decisions on. And all of this happens almost instantly (with an average latency of ~200ms 😮).
This tutorial will help you get comfortable with the EDA of Grandeur. Like the last time, we will build an app to publish data to a device in real-time, but this time, we'll put the event-driven nature of Grandeur to use. This tutorial is part of a whole introductory series and we urge you to checkout our previous one too where we simply published some data to a device through polling. If you’ve read it already or just want to skip over, keep reading 👇.
Step 1: Getting StartedWe will continue from where we left off and use the same project and device we created then. We were sending random data from a web app periodically every five seconds and were receiving it on our device by polling (device was asking Grandeur again and again if there is a new update) every five seconds. That method, while gets the work done, had one inherent problem. Since you have to check for the updates every few seconds, critical ones may not reach your device as frequently as they need to be. There will always be a delay equal to the time difference between when you polled and when the update really occurred. And if the updates occurred much more frequently (2–10x faster) than your polling interval, you will simply not know if those updates occurred at all.
This time we'll do things in a better way. Instead of polling to get updates from the internet, we will put up a listener which will get us the updates as soon as they occur, very much like interrupts.
Step 2: Web AppJust like the last time, we have two files: index.html
and main.js
and the code remains pretty much the same.
Here's the index.html
:
<!-- @file: Index.html -->
<!DOCTYPE html>
<html>
<!-- Head block of our page-->
<head>
<!-- Title of our page-->
<title>First Grandeur App</title>
<!-- Link to SDK's CDN -->
<script src="https://unpkg.com/grandeur-js"></script>
</head>
<!-- Body block of our page-->
<body>
<!-- Heading of the page -->
<h1>Events with Grandeur</h1>
<p id="status">Starting</p>
<!-- Link to the main script file -->
<script src="./main.js"></script>
</body>
</html>
and the main.js
:
/*
@file: main.js
Logic for the web app.
*/
/*
Gives the library your credentials to access your project
on your behalf.
*/
var project = grandeur.init(APIKEY, SECRET);
project.auth().token(AUTHTOKEN);
var timer = null;
/* Setting the connection status update handler */
project.onConnection((status) => {
/*
This callback gets fired
whenever the connection status
changes (app connects or disconnects
with Grandeur).
*/
switch(status) {
case "CONNECTED":
/*
If app is connected,
we display it on the app.
*/
document.getElementById("status").innerText = "Connected";
/*
Here we set up the timer to update data automatically every
5 seconds
*/
timer = setInterval(async function() {
/*
This function updates the device "state" variable
and set it to current datetime (we use datetime
because it's always changing).
*/
/* Gets our device */
var device = project.devices().device(DEVICEID);
/*
Changes device's "state" variable to Date.now()
(current datetime) and displays the new "state"
value on the browser's console.
*/
var state = Date.now();
await device.data().set("state", state);
console.log(state);
}, 5000);
break;
case "DISCONNECTED":
/* If app gets disconnected, we display the status
on the app and clear the timer to stop updating
device's "state" variable.
*/
document.getElementById("status").innerText = "Disconnected";
clearInterval(timer);
}
});
This app uses your hard-coded credentials (API Key, Secret, and Auth Token) to connect to your project on Grandeur. When it's connected, a timer starts updating the state
variable in device's data after every five seconds interval.
We start from the same Hardware.ino
as we created last time. But this time instead of polling for the data, we just subscribe to the state
variable and tell it which function to automatically call when an update occurs. This is the final code 👏
/* Including the Grandeur and WiFi libraries */
#include <Grandeur.h>
#include <ESP8266WiFi.h>
/* Configurations */
String apiKey = APIKEY;
String deviceID = DEVICEID;
String token = DEVICETOKEN;
/* WiFi credentials */
String ssid = WIFISSID;
String password = WIFIPASSWORD;
/* Create variable to hold project and device */
Grandeur::Project project;
Grandeur::Project::Device device;
/* Function to check device's connection status */
void onConnection(bool status) {
switch(status) {
case CONNECTED:
/* Device connected to internet */
Serial.println("Device is connected with internet.");
return;
case DISCONNECTED:
/* Device disconnected to internet */
Serial.println("Device is disconnected from the internet.");
return;
}
}
/* Function to handle the update in device state */
void updateHandler(const char* path, double state) {
/* Print state */
Serial.printf("Updated state is %f\n", state);
}
/* Function to connect to WiFi */
void connectWiFi() {
/* Set mode to station */
WiFi.mode(WIFI_STA);
/* Connect using the ssid and password */
WiFi.begin(ssid, password);
/* Block till WiFi connected */
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
/* Connected to WiFi so print message */
Serial.println("");
Serial.println("WiFi connected");
/* and IP address */
Serial.println(WiFi.localIP());
}
/* In setup */
void setup() {
/* Begin the serial */
Serial.begin(9600);
/* Connect to WiFi */
connectWiFi();
/* Gives the library the credentials to connect to Grandeur. */
project = grandeur.init(apiKey, token);
/* Sets which device? */
device = project.device(deviceID);
/* Sets connection update handler */
project.onConnection(onConnection);
/* Sets event handler on "state" update */
device.data().on("state", updateHandler);
}
/* Loop function */
void loop() {
/* This runs everything, so very important! */
if(WiFi.status() == WL_CONNECTED)
project.loop();
}
As soon as the device connects with WiFi, it uses the API key
and device token
to connect to our project on Grandeur. We then use the device.data().on()
function of the library to subscribe to state update and sets onUpdate
as the update handler function. An update handler must be a function that takes two arguments: path
of string type and data
of any type, and returns void
. path
is the name of the updated variable and data
is its updated value. All of these are valid update handler prototypes:
void updateHandler(const char* path, int temperature);
void updateHandler(const char* path, double voltage);
void updateHandler(const char* path, const char* label);
void updateHandler(const char* path, bool status);
Calling device.data().on()
subscribes to the device variable on Grandeur. Whenever the variable's value changes on Grandeur, the updateHandler
function is called right away with update.
It’s time to test our project. We can use node's http-server
or python's http.server
to locally serve our web app. If you have python, you can run this command in your app's folder:
python3 -m http.server 8000
This starts a local server in our folder. You can open the url localhost:8000
in your browser. And we see our app eventually getting connected with Grandeur. Then we plug our ESP into the laptop and open the serial monitor where we see our device establishing connection with Grandeur 🙌.
And the app starts sending datetime
to the device (browser window on the back) and the device receives it and prints it on the Serial Monitor (Arduino's monitor window on the front).
In this article, we updated a variable (state) from the app and and the update was propagated to our device which was listening for it. In a similar fashion, variables can be updated from the device-end and the app receives the updates if it’s listening for them on the other end. Besides device variables, the app can also set up update handlers on device name, online status, and more.
So this is it. About 99% of all communications in IoT happens as events and now you’ve mastered the art. Go build your own production grade IoT apps and hardware. Grandeur is free for students and IoT enthusiasts.
This tutorial is part of an introductory series and the best is still being brewed. In the next tutorial, we’ll go a step ahead of local development and see how you can deploy your web apps and host them on Grandeur with a single command.
You can go through the docs or checkout our youtube channel to learn to use the event-drive in complement with other features and build pompous apps in a snap. Ask your questions and give us your feedback at hi@grandeur.dev.
Comments