From 94de90b2b70cd761aa5c1281533255423a11d1cd Mon Sep 17 00:00:00 2001 From: abdullahmahboob Date: Wed, 9 Jun 2021 08:53:07 -0700 Subject: [PATCH 01/10] Updated version to 1.0.4 --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index a4c32c2..9f76c7e 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Grandeur -version=1.0.3 +version=1.0.4 author=Grandeur Technologies maintainer=Grandeur Technologies sentence=Let your arduinos and ESPs communicate with Grandeur in realtime. From 0efe4ff610435e1d1461cebeb199b7988892392b Mon Sep 17 00:00:00 2001 From: abdullahmahboob Date: Sat, 26 Jun 2021 03:45:30 -0700 Subject: [PATCH 02/10] Debug mode is turned off. Valve for the project.loop() method is now optional. You can also achieve the same thing by wrapping the project.loop() inside an if-statement. --- src/Grandeur.cpp | 5 +++++ src/Grandeur.h | 1 + src/debug.h | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Grandeur.cpp b/src/Grandeur.cpp index 93d9c5f..a248ad3 100644 --- a/src/Grandeur.cpp +++ b/src/Grandeur.cpp @@ -54,4 +54,9 @@ Grandeur::Project::Datastore Grandeur::Project::datastore(void) { void Grandeur::Project::loop(bool valve) { // Running duplex loop. _duplex->loop(valve); +} + +void Grandeur::Project::loop() { + // Running duplex loop. + _duplex->loop(true); } \ No newline at end of file diff --git a/src/Grandeur.h b/src/Grandeur.h index fcd3759..e901e20 100644 --- a/src/Grandeur.h +++ b/src/Grandeur.h @@ -56,6 +56,7 @@ class Grandeur::Project { // This method runs the SDK. void loop(bool valve); + void loop(); #if DEBUG // Defines these when in debug mode. diff --git a/src/debug.h b/src/debug.h index 9e9f52f..fe97286 100644 --- a/src/debug.h +++ b/src/debug.h @@ -1,6 +1,6 @@ // Debugging macros #define DEBUG 0 -#define DEBUG_WS 1 +#define DEBUG_WS 0 #if DEBUG || DEBUG_WS #define DEBUG_PORT Serial #endif /* DEBUG || DEBUG_WS */ From 2afa9d01e22ba176706e086029a0c4e77658de42 Mon Sep 17 00:00:00 2001 From: abdullahmahboob Date: Fri, 9 Jul 2021 05:41:51 -0700 Subject: [PATCH 03/10] Documentation in Readme.md is updated. --- README.md | 177 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 93 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index 0cf0285..f57c921 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ Grandeur is not a regular IoT cloud. It's a complete IoT product development and * No need to mix and match various services to come up with your own solution. Grandeur is a single spot solution for all of your needs, from **built-in authentication** of your users and devices to **an integrated database** to an **out-of-the-box file storage system** and a registry of data for all of your devices. And You can manage absolutely everything from a single dashboard. +* [Grandeur Canvas](https://grandeur.tech) is the latest addition to the Grandeur family. It lets you drag and drop widgets โ€” like buttons, sliders, displays, and graphs โ€” to sketch a layout which you can use in place of your app. This means you do not have to have app designers in your team before starting to build IoT โ€” a single hardware engineer is a beast with Grandeur. + * Simple pricing. Unlike Google and AWS, we do not have to deal with a different pricing model for each service and aggregate them together to compute the monthly bill making it almost impossible for the user to understand why he has to pay this much! Packaging all our services into one platform has let us develop a very simple and transparent pricing model. You can [start free][Grandeur Sign Up] for a certain quota and then pay as you go based on your resources consumption. Checkout [pricing][Grandeur Pricing] for more details. * We have a growing [community on Hackster][Grandeur Hackster] which is equivalent to growing number of developers which are using Grandeur and improving the opensource SDKs resulting in increasing Grandeur support. @@ -78,12 +80,10 @@ To get a deeper understanding of the core concepts Grandeur is built upon, dive * [device](#device) * [datastore](#datastore) * [Device](#device) - * [getSummary](#getsummary) - * [getParms](#getparms) - * [setSummary](#setsummary) - * [setParms](#setparms) - * [onSummary](#onsummary) - * [onParms](#onparms) + * [Data](#data) + * [get](#get) + * [set](#set) + * [on](#on) * [Datastore](#datastore) * [insert](#insert) * [Enhancements Under Consideration](#enhancements-under-consideration) @@ -777,30 +777,30 @@ So to allow a web app to interact with your project using the Web SDK, you first # Documentation -`Project` is the main class and all functionalities originate from it. You can safely imagine the object of `Project` class as a reference to your project on Grandeur. You get a reference to this object when you initialize SDK's configurations using `grandeur.init()`. +`Grandeur` is the entry point โ€” the door of the SDK. `grandeur` is the global object of the `Grandeur` that gets available right away when you include `` in your sketch. It has just one purpose and therefore gives you only one function: `grandeur.init()`. -`grandeur` is the global object that gets available right away when you include `` in your sketch. It has just one purpose and therefore gives you only one function: `grandeur.init()`. +`Project` is the main class and all functionalities originate from it. You can safely imagine the object of `Project` class as a reference to your project on Grandeur. You get the object of this class when you initialize SDK's configurations using `grandeur.init()`. -> ***Note 2***: You cannot connect with Grandeur or even with internet without first connecting with the WiFi. Therefore, the examples below are just for reference and you are required to handle your device's WiFi in order for things to work. You can see [these examples][Examples] to get a deeper understanding. They do WiFi handling too. ๐Ÿ˜‰ +> ***Note 2***: You cannot connect with Grandeur or even with internet without first connecting with the WiFi. Therefore, the examples below are just for reference and you are required to handle your device's WiFi yourself in order for things to work. You can see [these examples][Examples] to get a deeper understanding. They do WiFi handling too. ๐Ÿ˜‰ ### init > grandeur.init (apiKey: _String_, token: _String_) : returns _Project_ -This method initializes SDK's connection configurations: `apiKey` and `authToken`, and returns a reference to object of the `Project` class. `Project` class is the main class that exposes all functions of the SDK. +This method initializes SDK's connection configurations: `apiKey` and `authToken`, and returns an object of the `Project` class. `Project` class is the main class that exposes all functions you can perform on your project resources through the SDK. #### Parameters | Name | Type | Description | |-------------|----------|-----------------------------------------------------------------| -| apiKey | _String_ | API key of your project that your device belongs to | -| token | _String_ | Access token generated when the device is paired with the user | +| apiKey | _String_ | API key of your project that your device is registered in. | +| token | _String_ | Access token generated when the device is paired with a user. | #### Example ```cpp // Container for the object of GrandeurDevice class. -Project myProject; +Grandeur::Project myProject; void setup() { myProject = grandeur.init(YourApiKey, YourToken); } @@ -812,20 +812,20 @@ void setup() { ## Project -Project is the main class of the SDK. When SDK connects with Grandeur, this class represents your cloud project, tuned down to the device scope. There are only two APIs you can interact with: device and datastore, which are represented by their respective classes. +Project is the main class of the SDK. When SDK connects with Grandeur, this class represents your grandeur project, from the device's perspective โ€” there are only two resources your device can interact with: **device** and **datastore**, which are represented by their respective classes. -This class exposes the following methods: +Project class exposes the following methods: ### isConnected > isConnected(void): returns _bool_ -This method returns true if the SDK is connected with Grandeur. +This method returns true if the device is connected with Grandeur. #### Example ```cpp -Project myProject; +Grandeur::Project myProject; void setup() { myProject = grandeur.init(YourApiKey, YourToken); } @@ -851,19 +851,19 @@ void loop() { > onConnection (callback : _Callback_) : returns _void_ -This method schedules a function to be called when the SDK's connection with Grandeur is made or broken. The function passed to it as argument is called an **event handler** for it handles events like connection/disconnection with Cloud. Example below illustrates its usage. +This method schedules a function to be called when the device's connection with Grandeur is made or broken. The function passed to it as argument is called an **event handler** for it handles events like connection/disconnection with Grandeur. Example below illustrates its usage. #### Parameters | Name | Type | Description | |-------------|------------------|--------------------------------------------------------------------------------| -| callback | _void (*)(bool)_ | An event handler function for device's connection/disconnection with Grandeur | +| callback | _void (*)(bool)_ | An event handler function for device's connection/disconnection with Grandeur. | #### Example ```cpp -Project myProject; +Grandeur::Project myProject; void connectionCallback(bool status) { // This method handles the events related to device's connection with Grandeur. @@ -898,21 +898,21 @@ void loop() { > loop (valve: _bool_) : returns _void_ -This method is the legs of the SDK. Without it, the SDK can't run. Therefore, it must be called in Arduino's `loop()` and without being suspected to any *delay*. **This method is what runs the underlying event loop and makes all the *Async* functions possible.** -It also accepts an argument which we call **valve**. A **valve** is a boolean expression whose value decides if the SDK would run for this `loop` or not. For example, we can use it to dictate to the SDK to run only when the device WiFi is connected. +This method is the legs of the SDK. Without it, the SDK doesn't run. Therefore, it must be called in Arduino's `loop()` and without being suspected to any *delay*. **This method is what runs the underlying event loop and makes all the *Async* functions possible.** +It can also accept an argument which we call **valve**. A **valve** is a boolean expression whose value decides if the SDK would run for current `loop` or not. For example, we can use it to dictate to the SDK to run only when the device WiFi is connected. -> **A Tidbit:** [Here][Using Millis Instead of Delay] is how you can use `millis()` instead of `delay()` if you want a function to run after every few moments. +> **A Tidbit:** [Here][Using Millis Instead of Delay] is how you can use `millis()` instead of `delay()` if you want a function to run after every few moments without blocking the loop. #### Example ```cpp -Project myProject; +Grandeur::Project myProject; void setup() { myProject = grandeur.init(YourApiKey, YourToken); } void loop() { - myProject.loop(WiFiState == CONNECTED); + myProject.loop(WiFiState == CONNECTED); // Same as "if(WiFiState == CONNECTED) myProject.loop();" } // **RESULT** // Runs the SDK only when the WiFi is connected. @@ -922,13 +922,13 @@ void loop() { > device (deviceID: _String_) : returns _Device_ -This method returns a reference to object of the **Device** class. Read about **Device** class [here][Device Class]. +This method returns an object of the **Device** class. Read about **Device** class [here][Device Class]. #### Example ```cpp -Project myProject; -Device myDevice; +Grandeur::Project myProject; +Grandeur::Project::Device myDevice; void setup() { myProject = grandeur.init(YourApiKey, YourToken); myDevice = myProject.device(YourDeviceID); @@ -947,8 +947,8 @@ This method returns a reference to object of the **Datastore** class. Datastore #### Example ```cpp -Project myProject; -Datastore myDatastore; +Grandeur::Project myProject; +Grandeur::Project::Datastore myDatastore; void setup() { myProject = grandeur.init(YourApiKey, YourToken); myDatastore = myProject.datastore(); @@ -960,13 +960,14 @@ void setup() { ## Device -Device class exposes the functions of the device API. Its data function returns a reference to object of `Data` class which represents device's data space. You can use it to update device variables on Grandeur, pulling variables from Grandeur, listening for updates in your device variables, etc. +Device class gives you the functions to interact with your device metadata and data. Its `data` function returns the object of `Data` class that gives you the functions to get, set, and subscribe to the device's data variables. Subscribe means if you update this device's data variables from anywhere other than this device itself, this device will get the update. This is great for realtime switching ON/OFF of your device remotely from an app or changing voltage of its pins for example. -Device's `Data` class exposes the following functions: +Here's how you can use each function of the `Data` class: ### get > get (path: _String_, callback: _Callback_) : returns _void_ +> get (callback: _Callback_) : returns _void_ This method gets a device variable from Grandeur. @@ -980,11 +981,11 @@ This method gets a device variable from Grandeur. #### Example ```cpp -Project myProject; -Device myDevice; +Grandeur::Project myProject; +Grandeur::Project::Device myDevice; void getVoltageCallback(Var result) { - // This method just prints *voltage* variable from the device's summary. + // This method prints *voltage* variable from device data. Serial.println(result["data"]<<"\n"); } @@ -994,39 +995,42 @@ void setup() { } void loop() { - // This gets the summary on every loop and calls getSummaryCallback() function when its - // response from the cloud is received. + // This requests to get "voltage" variable from device data on every loop and calls getVoltageCallback() function when the + // data from Grandeur actually arrives. myDevice.data().get("voltage", getVoltageCallback); - myProject.loop(true); + if(WiFiIsConnected) myProject.loop(); } // **RESULT** // Prints the value of the voltage variable from Grandeur on every loop. ``` +You can get all device data variables with the `path`-less `get` function overload. + ### set > set (path : _String_, data : _any_, callback: _Callback_) : returns _void_ +> set (path : _String_, data : _any_) : returns _void_ This method updates a device variable on Grandeur with new data. #### Parameters -| Name | Type | Description | -|-------------|---------------|--------------------------------------------------------------| -| path | _String_ | Path of the device variable using dot notation | -| data | _Var_ | New data to store in the variable | -| callback | _Callback_ | A function to be called when set response is received | +| Name | Type | Description | +|-------------|---------------|--------------------------------------------------------------------------| +| path | _String_ | Path of the device variable using dot notation. | +| data | _Var_ | New data to store in the variable. | +| callback | _Callback_ | A function to be called after the variable at `path` is actually updated.| #### Example ```cpp -Project myProject; -Device myDevice; +Grandeur::Project myProject; +Grandeur::Project::Device myDevice; void setVoltageCallback(Var result) { - // This method prints *voltage* value after it is updated on Grandeur. + // This method prints "voltage" value after it is updated on Grandeur. Serial.println(result["update"]); } @@ -1038,24 +1042,29 @@ void setup() { void loop() { // Reading pin value. int voltage = analogRead(A0); - // This sets the summary on every loop and calls setSummaryCallback() function when its - // response from the cloud is received. + // This requests to set the "voltage" variable on every loop and calls setVoltageCallback() function when the + // variable is actually updated on Grandeur. myDevice.data().set("voltage", voltage, setVoltageCallback); - myProject.loop(true); + if(WiFiIsConnected) myProject.loop(); } // **RESULT** -// Setting the summary and prints the updated values of the summary -// variables (voltage and current in our case) on every loop. +// Sets the voltage variable and prints its updated value +// after it's updated on Grandeur, on every loop. ``` +If you do not need to do anything after the successful update of a data variable, you can use the `callback`-less `set` function overload. + ### on > on (path: _String_, callback : _Callback_) : returns _void_ +> on (callback : _Callback_) : returns _void_ This method schedules a function to be called when a device variable changes on Grandeur. +A variable can be changed by a paired user from an app, dashboard, or any SDKs. All three of these updates would trigger the on-callback, or you can call it the device's data update handler. But updating a device's data variable from the device itself won't trigger its own update handler. + > ***A Tidbit***: *Update is a special type of event* and the function that handles it is called an **update handler**. #### Parameters @@ -1070,60 +1079,60 @@ More on Callback [here][callback]. #### Example ```cpp -Project myProject; -Device myDevice; +Grandeur::Project myProject; +Grandeur::Project::Device myDevice; void voltageUpdatedCallback(Var result) { - // When summary update occurs on Grandeur, this function extracts the updated values of - // voltage and current, and sets the corresponding pins. - Serial.println("Summary update occurred!\n"); - int voltage = updatedSummary["voltage"]; - digitalWrite(voltage, A0); + // When "voltage" update occurs on Grandeur, this function extracts + // its updated value and writes it to an analog pin. + Serial.println("Voltage update occurred!\n"); + int voltage = result["voltage"]; + analogWrite(voltage, A0); } void setup() { myProject = grandeur.init(YourApiKey, YourToken); myDevice = myProject.device(YourDeviceID); - myDevice.on("voltage", voltageUpdatedCallback); + myDevice.data().on("voltage", voltageUpdatedCallback); } void loop() { - myProject.loop(true); + if(WiFiIsConnected) myProject.loop(); } ``` +You can subscribe to all device data variables with the `path`-less `on` function overload. + ## Datastore -Device class exposes the functions of the device API. It generally handles your device's data on Grandeur like: updating device variables on Grandeur, pulling variables from Grandeur, listening for cloud updates in your device variables, and so on. +Datastore class gives you the functions to store and search for data in Grandeur datastore which a highly scalable database where you can log data points and retrieve them later to plot trend or device health graphs. Read about Datastore [here](https://www.hackster.io/grandeurtech/data-persistence-in-iot-with-grander-fd09ee). -It exposes the following functions: +Datastore class exposes the following functions: ### insert > insert (documents: _Var_, callback: _Callback_) : returns _void_ -This method inserts documents into Grandeur datastore. +This method inserts documents into datastore. #### Parameters -| Name | Type | Description | -|-------------|--------------|--------------------------------------------------------------------------| -| documents | _Var_ | An array of documents (_Var_ s) to be inserted into the datastore | -| callback | _Callback_ | A function to be called when insertion of documents is completed | +| Name | Type | Description | +|-------------|--------------|--------------------------------------------------------------------| +| documents | _Var_ | An array of documents (_Var_ s) to be inserted into the datastore. | +| callback | _Callback_ | A function to be called when insertion of documents completes. | #### Example ```cpp -Project myProject; -Datastore myDatastore; +Grandeur::Project myProject; +Grandeur::Project::Datastore myDatastore; void insertCallback(Var insertionResult) { // This method just prints if the insertion is successful or not. - if(insertionResult["code"] == "DATASTORE-DOCUMENTS-INSERTED") { - Serial.println("Insertion successful."); - } - Serial.println("Insertion Failed."); + if(insertionResult["code"] == "DATASTORE-DOCUMENTS-INSERTED") Serial.println("Insertion successful."); + else Serial.println("Insertion Failed."); } void setup() { @@ -1143,7 +1152,7 @@ void loop() { // completes. myDatastore.insert(docs, insertCallback); - myProject.loop(true); + if(WiFiIsConnected) myProject.loop(); } // **RESULT** @@ -1155,22 +1164,22 @@ void loop() { > search (filter: _Var_, projection: _Var_, pageNumber: _int_, callback: _Callback_) : returns _void_ -This method searches for documents in Grandeur datastore based on the `filter` supplied. `Filter` describes what documents to return and `projection` describes what fields to mask/unmask in those documents. Their is a limit on the how many documents can be returned in one query. The documents are divided into pages and you can get subsequent pages by specifying the `pageNumber`. +This method searches for documents in datastore based on the `filter` supplied. `Filter` describes what documents to return and `projection` describes what fields to return in those documents. Documents are returned in pages and each page is **20 documents** in size. This is what the `pageNumber` is for. You'll get first page by specifying the `pageNumber` to 0 and 1 for second page. and so on. #### Parameters -| Name | Type | Description | -|-------------|--------------|--------------------------------------------------------------------------------------------| -| filter | _Var_ | A document that describes the conditions which the documents should satisfy to be returned | -| projection | _Var_ | A document that describes what fields to return | -| pageNumber | _int_ | Number of the requested page | -| callback | _Callback_ | A function to be called when insertion of documents is completed | +| Name | Type | Description | +|-------------|--------------|----------------------------------------------------------------------------------------| +| filter | _Var_ | A document describing the conditions that the documents to be returned are to satisfy. | +| projection | _Var_ | A document that describes what fields to return. | +| pageNumber | _int_ | Number of the page to return. | +| callback | _Callback_ | A function to be called when documents are completed. | #### Example ```cpp -Project myProject; -Datastore myDatastore; +Grandeur::Project myProject; +Grandeur::Project::Datastore myDatastore; void searchCallback(Var searchResult) { // This method just prints the documents if the search is successful. @@ -1198,7 +1207,7 @@ void loop() { // This fetches 1st page of all the documents stored in the datastore. myDatastore.collection("myCollectionName").search({}, {}, 0, searchCallback); - myProject.loop(true); + if(WiFiIsConnected) myProject.loop(); } // **RESULT** From 72aeb762b82930b6df7d01a75beec2b80842d55e Mon Sep 17 00:00:00 2001 From: abdullahmahboob Date: Fri, 9 Jul 2021 06:12:33 -0700 Subject: [PATCH 04/10] Broken links in Readme.md are resolved. --- README.md | 54 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index f57c921..9b26d6f 100644 --- a/README.md +++ b/README.md @@ -72,20 +72,21 @@ To get a deeper understanding of the core concepts Grandeur is built upon, dive * [Networking](#networking) * [Allowed Origins](#allowed-origins) * [Documentation](#documentation) - * [init](#grandeur-init) + * [init](#init) * [Project](#project) * [isConnected](#isconnected) * [onConnection](#onconnection) * [loop](#loop) - * [device](#device) + * [device](#:~:text=device%20(deviceID:%20_String_)%20:%20returns%20_Grandeur::Project::Device_) * [datastore](#datastore) - * [Device](#device) + * [Device](#:~:text=Device%20class%20gives%20you%20the%20functions) * [Data](#data) * [get](#get) * [set](#set) * [on](#on) * [Datastore](#datastore) * [insert](#insert) + * [search](#search) * [Enhancements Under Consideration](#enhancements-under-consideration) ## Get Started @@ -779,13 +780,16 @@ So to allow a web app to interact with your project using the Web SDK, you first `Grandeur` is the entry point โ€” the door of the SDK. `grandeur` is the global object of the `Grandeur` that gets available right away when you include `` in your sketch. It has just one purpose and therefore gives you only one function: `grandeur.init()`. -`Project` is the main class and all functionalities originate from it. You can safely imagine the object of `Project` class as a reference to your project on Grandeur. You get the object of this class when you initialize SDK's configurations using `grandeur.init()`. +`Project` is the main class and all grandeur functionalities originate from it. You can safely imagine the object of `Project` class as a reference to your project on Grandeur. You get the object of this class when you initialize SDK's configurations using `grandeur.init()`. > ***Note 2***: You cannot connect with Grandeur or even with internet without first connecting with the WiFi. Therefore, the examples below are just for reference and you are required to handle your device's WiFi yourself in order for things to work. You can see [these examples][Examples] to get a deeper understanding. They do WiFi handling too. ๐Ÿ˜‰ +## Grandeur + +Grandeur class exposes one method โ€” `init` โ€” and one class โ€” `Project`: ### init -> grandeur.init (apiKey: _String_, token: _String_) : returns _Project_ +> grandeur.init (apiKey: _String_, token: _String_) : returns _Grandeur::Project_ This method initializes SDK's connection configurations: `apiKey` and `authToken`, and returns an object of the `Project` class. `Project` class is the main class that exposes all functions you can perform on your project resources through the SDK. @@ -799,7 +803,7 @@ This method initializes SDK's connection configurations: `apiKey` and `authToken #### Example ```cpp -// Container for the object of GrandeurDevice class. +// Container for the object of Device class. Grandeur::Project myProject; void setup() { myProject = grandeur.init(YourApiKey, YourToken); @@ -812,7 +816,7 @@ void setup() { ## Project -Project is the main class of the SDK. When SDK connects with Grandeur, this class represents your grandeur project, from the device's perspective โ€” there are only two resources your device can interact with: **device** and **datastore**, which are represented by their respective classes. +Project is the main class of the SDK. When your device connects with Grandeur, this class represents your grandeur project, from the device's perspective โ€” there are only two resources your device can interact with: **device** and **datastore**, which are represented by their respective classes. Project class exposes the following methods: @@ -920,9 +924,9 @@ void loop() { ### device -> device (deviceID: _String_) : returns _Device_ +> device (deviceID: _String_) : returns _Grandeur::Project::Device_ -This method returns an object of the **Device** class. Read about **Device** class [here][Device Class]. +This method returns an object of the **Device** class. #### Example @@ -940,7 +944,7 @@ void setup() { ### datastore -> datastore (void) : returns _Datastore_ +> datastore (void) : returns _Grandeur::Project::Datastore_ This method returns a reference to object of the **Datastore** class. Datastore class exposes the functions of the datastore API which handles your queries to your project's datastore like: logging device variables to the cloud datastore, searching for data, etc. @@ -960,7 +964,35 @@ void setup() { ## Device -Device class gives you the functions to interact with your device metadata and data. Its `data` function returns the object of `Data` class that gives you the functions to get, set, and subscribe to the device's data variables. Subscribe means if you update this device's data variables from anywhere other than this device itself, this device will get the update. This is great for realtime switching ON/OFF of your device remotely from an app or changing voltage of its pins for example. +Device class gives you the functions to interact with your device data. Its `data` function returns the object of `Data` class that gives you the functions to get, set, and subscribe to the device's data variables. Subscribe means if you update this device's data variables from anywhere other than this device itself, this device will get the update. This is great for realtime switching ON/OFF of your device remotely from an app or changing voltage of its pins for example. + +Device class exposes only the `data` function: + +### data + +> data () : returns _Grandeur::Project::Device::Data_ + +This method returns an object of the **Data** class. + +#### Example + +```cpp +Grandeur::Project myProject; +Grandeur::Project::Device myDevice; +Grandeur::Project::Device::Data myDeviceData; +void setup() { + myProject = grandeur.init(YourApiKey, YourToken); + myDevice = myProject.device(YourDeviceID); + myDeviceData = myDevice.data(); +} + +// **RESULT** +// Gets the object of the Device Data class. +``` + +## Data + +`Data` class gives you the functions to get, set, and subscribe to the device's data variables. Here's how you can use each function of the `Data` class: From 86bdfba37fd8213fa7f824d4613ade7dc7847fd9 Mon Sep 17 00:00:00 2001 From: abdullahmahboob Date: Fri, 9 Jul 2021 06:16:29 -0700 Subject: [PATCH 05/10] Broken links in Readme.md are resolved. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b26d6f..2ea9a00 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ To get a deeper understanding of the core concepts Grandeur is built upon, dive * [loop](#loop) * [device](#:~:text=device%20(deviceID:%20_String_)%20:%20returns%20_Grandeur::Project::Device_) * [datastore](#datastore) - * [Device](#:~:text=Device%20class%20gives%20you%20the%20functions) + * [Device](#:~:text=Device%20class%20gives%20you%20the%20functions%20to%20interact%20with%20your%20device%20data.) * [Data](#data) * [get](#get) * [set](#set) From f62ef2bdf59b46fc3805bca7eb167c6cec724f2e Mon Sep 17 00:00:00 2001 From: abdullahmahboob Date: Fri, 9 Jul 2021 06:23:32 -0700 Subject: [PATCH 06/10] Broken links in Readme.md are resolved. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2ea9a00..4d6e9cb 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ To get a deeper understanding of the core concepts Grandeur is built upon, dive * [loop](#loop) * [device](#:~:text=device%20(deviceID:%20_String_)%20:%20returns%20_Grandeur::Project::Device_) * [datastore](#datastore) - * [Device](#:~:text=Device%20class%20gives%20you%20the%20functions%20to%20interact%20with%20your%20device%20data.) + * [Device](#device-class) * [Data](#data) * [get](#get) * [set](#set) @@ -962,7 +962,7 @@ void setup() { // Gets the object of Datastore class. ``` -## Device +## Device Device class gives you the functions to interact with your device data. Its `data` function returns the object of `Data` class that gives you the functions to get, set, and subscribe to the device's data variables. Subscribe means if you update this device's data variables from anywhere other than this device itself, this device will get the update. This is great for realtime switching ON/OFF of your device remotely from an app or changing voltage of its pins for example. From a2ff8ad723cd6129b8d3b2fb4aed825d2075fec9 Mon Sep 17 00:00:00 2001 From: abdullahmahboob Date: Fri, 9 Jul 2021 06:26:12 -0700 Subject: [PATCH 07/10] Broken links in Readme.md are resolved. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4d6e9cb..54b791c 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ To get a deeper understanding of the core concepts Grandeur is built upon, dive * [isConnected](#isconnected) * [onConnection](#onconnection) * [loop](#loop) - * [device](#:~:text=device%20(deviceID:%20_String_)%20:%20returns%20_Grandeur::Project::Device_) + * [device](#device-function) * [datastore](#datastore) * [Device](#device-class) * [Data](#data) @@ -922,7 +922,7 @@ void loop() { // Runs the SDK only when the WiFi is connected. ``` -### device +### device > device (deviceID: _String_) : returns _Grandeur::Project::Device_ From 387826692429ab49138cae3d3b6514c17d44e0f7 Mon Sep 17 00:00:00 2001 From: abdullahmahboob Date: Sat, 10 Jul 2021 01:00:27 -0700 Subject: [PATCH 08/10] Readme.md is updated. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 54b791c..8cbdaec 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Grandeur is not a regular IoT cloud. It's a complete IoT product development and * No need to mix and match various services to come up with your own solution. Grandeur is a single spot solution for all of your needs, from **built-in authentication** of your users and devices to **an integrated database** to an **out-of-the-box file storage system** and a registry of data for all of your devices. And You can manage absolutely everything from a single dashboard. -* [Grandeur Canvas](https://grandeur.tech) is the latest addition to the Grandeur family. It lets you drag and drop widgets โ€” like buttons, sliders, displays, and graphs โ€” to sketch a layout which you can use in place of your app. This means you do not have to have app designers in your team before starting to build IoT โ€” a single hardware engineer is a beast with Grandeur. +* [Grandeur Canvas](https://grandeur.tech) is the latest addition to the Grandeur family. It lets you drag and drop widgets โ€” like buttons, sliders, displays, and graphs โ€” to sketch a layout which you can use instead of coding a full app. This means you do not have to have app designers in your team before starting to build IoT โ€” a single hardware engineer is a beast with Grandeur. * Simple pricing. Unlike Google and AWS, we do not have to deal with a different pricing model for each service and aggregate them together to compute the monthly bill making it almost impossible for the user to understand why he has to pay this much! Packaging all our services into one platform has let us develop a very simple and transparent pricing model. You can [start free][Grandeur Sign Up] for a certain quota and then pay as you go based on your resources consumption. Checkout [pricing][Grandeur Pricing] for more details. From 7cae3b49b3d06dad3a489876f3b1a5fa514c53cc Mon Sep 17 00:00:00 2001 From: Muhammad Abdullah Date: Sat, 10 Jul 2021 12:25:07 -0700 Subject: [PATCH 09/10] Update README.md --- README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8cbdaec..5769820 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Grandeur is designed keeping in mind all the challenges a hardware engineer can For example, you can use the **Auth API** to create *register* and *login* flows and make sure each user has access to its own data and no one other than the device admin itself should be able to interact with its device. You can store a humongous amount of data in cloud database to analyze and extract intelligent information from it and display useful graphs. Use our **datastore API** for that. You can host your product's website and your web app on Grandeur as well. It's **as simple as running literally a single command**. Also, your hardware device can listen for events and updates from your app, your app can listen for events and updates from your hardware device, and they can communicate with each other in realtime (with a latency of ~200ms). **Devices API** and **Device SDK** come into play here. But in no way would you have to waste your time in mixing and matching the APIs, checking which one works for your use case, and go through a huge learning curve -- like you would do while working with AWS or Google Cloud Platform. All the Grandeur APIs are completely integrated and speed and security is built in. The SDKs are designed around the whole ideology of **seamless integration.** -Grandeur is not a regular IoT cloud. It's a complete IoT product development and management platform, designed for production environments. Here's how: +Grandeur is not a regular IoT backend. It's a complete IoT product development and management platform, designed for production environments. Here's how: * Grandeur is product-centered. It is much more than just a medium of communication between your app and your hardware device. Focusing on expediting IoT product development, it offers an ecosystem of the most necessary tools integrated to make the most head-cracking development problems seamless. What problems you may ask? * Huge development stack (Your IoT product which is generally hardware, your web app, your server API, and the communication between all of these). @@ -26,7 +26,7 @@ Grandeur is not a regular IoT cloud. It's a complete IoT product development and * [Grandeur Canvas](https://grandeur.tech) is the latest addition to the Grandeur family. It lets you drag and drop widgets โ€” like buttons, sliders, displays, and graphs โ€” to sketch a layout which you can use instead of coding a full app. This means you do not have to have app designers in your team before starting to build IoT โ€” a single hardware engineer is a beast with Grandeur. -* Simple pricing. Unlike Google and AWS, we do not have to deal with a different pricing model for each service and aggregate them together to compute the monthly bill making it almost impossible for the user to understand why he has to pay this much! Packaging all our services into one platform has let us develop a very simple and transparent pricing model. You can [start free][Grandeur Sign Up] for a certain quota and then pay as you go based on your resources consumption. Checkout [pricing][Grandeur Pricing] for more details. +* Simple pricing. Unlike Google and AWS, we do not have to deal with a different pricing model for each service and aggregate them together to compute the monthly bill making it almost impossible for the user to understand why he has to pay this much! Focusing just on solving IoT has let us develop a very simple and transparent pricing model. You can [start free][Grandeur Sign Up] for a certain quota and then pay as you go based on your resources consumption. Checkout [pricing][Grandeur Pricing] for more details. * We have a growing [community on Hackster][Grandeur Hackster] which is equivalent to growing number of developers which are using Grandeur and improving the opensource SDKs resulting in increasing Grandeur support. @@ -36,6 +36,8 @@ Follow [our Hackster Hub][Grandeur Hackster] for quick starts and advanced devel [Here][Get Started With Grandeur] is how you can create a new project on Grandeur and start using the Javascript SDK to build your IoT apps. +[Here][Get Started With Grandeur Canvas] is how you can drag-and-drop your app in seconds. + From here onwards, we'll look at how you can use the Arduino SDK for all arduino-compatible modules to put your devices live and connected on Grandeur. Let's dive in! # Arduino SDK @@ -95,11 +97,11 @@ To get a deeper understanding of the core concepts Grandeur is built upon, dive 1. You can search for **Grandeur** in Arduino's library manager. In Arduino IDE, open `Sketch > Include Library > Manage Libraries` and install Grandeur from there. -2. You can also clone **Arduino SDK** from [here][Arduino SDK] and [install it in your Arduino IDE][Installing an Arduino Library]. +2. You can also download the latest release of **Arduino SDK** from [here][Latest Release] and [install it in your Arduino IDE][Installing an Arduino Library]. ### Inclusion -When you include `` in your sketch, a global object `grandeur` is created right away which you can use to initialize the SDK's configurations. +When you include `` in your sketch, a global object `grandeur` is created right away which you can use to give the SDK credentials so it can start connecting with Grandeur (more on it in the next step). ```cpp #include @@ -110,7 +112,7 @@ When you include `` in your sketch, a global object `grandeur` is cr ### Initialization -Initialization is as simple as calling `grandeur.init()` with your credentials (Project's API Key and Device's Access Token). The SDK uses your API key to select your project, and device ID and access token to limit its scope to only your device's data. It then returns a `Project` object which exposes other subclasses like `Device` and `Datastore`, and you can go programming your device from there. +Initialization is as simple as calling `grandeur.init()` with your credentials (Your project's API Key and device's Access Token). The SDK uses your API key to know which project, and device ID and access token to know which device. It then returns a `Project` object which exposes other subclasses like `Device` and `Datastore`, and you can go programming your device from there. ```cpp #include @@ -1262,6 +1264,7 @@ Here are some enhancements that we are considering to implement in the SDK. They [Grandeur Settings]: https://cloud.grandeur.tech/settings "Grandeur Settings" [Grandeur Pricing]: https://grandeur.tech/pricing/ "Pricing" [Get Started With Grandeur]: https://github.com/grandeurtech/js-sdk#get-started "Get Started With Grandeur" +[Get Started With Grandeur Canvas]: https://youtu.be/gqTmtDJkPl8 "Get Started With Grandeur Canvas" [An Example Webapp]: https://github.com/grandeurtech/js-sdk#example "An Example Webapp" [Examples]: https://github.com/grandeurtech/arduino-sdk/tree/master/examples/ [Arduino IDE]: https://www.arduino.cc/en/main/software "Arduino IDE" @@ -1284,6 +1287,7 @@ Here are some enhancements that we are considering to implement in the SDK. They [Get Started with Arduino SDK]: #get-started "Get Started with Arduino SDK" [Arduino SDK]: https://github.com/grandeurtech/arduino-sdk "Arduino SDK" +[Latest Release]: https://github.com/grandeurtech/arduino-sdk/releases/latest "Arduino SDK's Latest Release" [project]: #project "Project" [summary]: #device-registry "Summary" [parms]: #device-registry "Parms" From bf15249d1856758ad288969e5440b16bfef5ddfd Mon Sep 17 00:00:00 2001 From: Muhammad Abdullah Date: Sat, 17 Jul 2021 20:16:17 +0500 Subject: [PATCH 10/10] Readme.md is updated. --- README.md | 249 +++++++++++++++++++++++++++--------------------------- 1 file changed, 123 insertions(+), 126 deletions(-) diff --git a/README.md b/README.md index 5769820..386613d 100644 --- a/README.md +++ b/README.md @@ -55,10 +55,10 @@ To get a deeper understanding of the core concepts Grandeur is built upon, dive * [Inclusion](#inclusion) * [Initialization](#initialization) * [Handling the WiFi](#handling-the-wifi) - * [Setting Up the Valve](#setting-up-the-valve) - * [Events Listening](#events-listening) + * [Sending Messages Only When the WiFi is Connected](#sending-messages-only-when-the-wifi-is-connected) + * [Handling Connection and Disconnection with Grandeur](#handling-connection-and-disconnection-with-grandeur) * [Fetching Device Variables and Updating Them](#fetching-device-variables-and-updating-them) - * [Handling Updates From Grandeur](#handling-updates-from-the-cloud) + * [Handling Updates From Grandeur](#handling-updates-from-grandeur) * [Example](#example) * [The Dexterity of Arduino SDK](#the-dexterity-of-arduino-sdk) * [Grandeur Ecosystem](#grandeur-ecosystem) @@ -101,7 +101,7 @@ To get a deeper understanding of the core concepts Grandeur is built upon, dive ### Inclusion -When you include `` in your sketch, a global object `grandeur` is created right away which you can use to give the SDK credentials so it can start connecting with Grandeur (more on it in the next step). +When you include `` in your sketch, a global object `grandeur` is created right away which you can use to give the SDK [your project's API Key](https://cloud.grandeur.tech/settings/#:~:text=Project%20API%20Key) and [your device access token](https://cloud.grandeur.tech/devices/#:~:text=Register%20Device) so it can start connecting with Grandeur (more on it in the next step). ```cpp #include @@ -112,12 +112,12 @@ When you include `` in your sketch, a global object `grandeur` is cr ### Initialization -Initialization is as simple as calling `grandeur.init()` with your credentials (Your project's API Key and device's Access Token). The SDK uses your API key to know which project, and device ID and access token to know which device. It then returns a `Project` object which exposes other subclasses like `Device` and `Datastore`, and you can go programming your device from there. +Initialization is as simple as calling `grandeur.init()` with your credentials (Your project's API Key and device's Access Token). The SDK uses your API key to know which project and access token to make sure the request is coming from a legit source. It then returns a `Grandeur::Project` object which exposes other subclasses like `Device` and `Datastore`, and you can go programming your device from there. ```cpp #include -Project myProject; +Grandeur::Project myProject; void setup() { // You can initialize device configurations like this. @@ -146,7 +146,7 @@ Here we illustrate how to handle your ESP8266's WiFi. #include #include -Project myProject; +Grandeur::Project myProject; void setupWiFi(void) { Serial.begin(9600); @@ -169,22 +169,18 @@ void setup() { void loop() { // This runs the SDK when the device WiFi is connected. - myProject.loop(WiFi.status() == WL_CONNECTED); + if(WiFi.status() == WL_CONNECTED) myProject.loop(); } ``` -### Setting Up the Valve - -You can see this line in the previous subsection: `myProject.loop(WiFi.status() == WL_CONNECTED)`, but what does that mean? +### Sending Messages Only When the WiFi is Connected -`loop` function is what runs the SDK: it connects with the cloud; when disconnected, it automatically reconnects; pulls new messages from the cloud; pushes messages to the cloud; and so on. But doing any sort of communication on the internet is useless until the WiFi isn't connected. That's exactly what the statement does: it acts like a **valve** for the SDK. The conditional expression passed to the `loop` function decides when the SDK would run and when it would not. In this case, it would only run when the WiFi is connected, causing `WiFi.status() == WL_CONNECTED` expression to evaluate to `true`. If while running, the WiFi gets disconnected, `WiFi.status() == WL_CONNECTED` would evaluate to `false` and the SDK would stop running. +`loop` function is what runs the SDK: it connects the device with Grandeur; when disconnected, it automatically reconnects; pulls new messages from Grandeur; pushes messages to Grandeur; and so on. But trying to do any sort of communication on the internet is useless until the WiFi isn't connected. That's exactly what the statement `if(WiFi.status() == WL_CONNECTED) myProject.loop()` does. The if-statement decides when the SDK should run and when it should not. In this case, it would only run when the WiFi is connected. If while running, the WiFi gets disconnected, `WiFi.status() == WL_CONNECTED` would evaluate to `false` and the SDK would stop running (trying to send more messages or fetch data). -### Setting Up Connection Event Handler +### Handling Connection and Disconnection with Grandeur -You can also listen on SDK's connection-related events. For example, to run some code when the device makes a successful connection to the cloud or when the device's connection to the cloud breaks, you can wrap that code in a `Callback` function and pass it to `Project`'s `onConnection()` function. - -The `Callback` function is a special type of function that accepts a `Var` as a parameter and returns `void`. Read more about `Callback` and `Var` [here][var]. +You can also listen on SDK's connection-related events. For example, to run some code when the device makes a successful connection to Grandeur or when the device's connection to Grandeur breaks, you can wrap that code in "a function that takes a `bool` and returns `void`" and pass it to `Grandeur::Project`'s `onConnection()` function. Here's how you can handle the connection event: @@ -192,7 +188,7 @@ Here's how you can handle the connection event: #include #include -Project myProject; +Grandeur::Project myProject; void setupWiFi(void) { Serial.begin(9600); @@ -231,20 +227,20 @@ void setup() { void loop() { // This runs the SDK when the device WiFi is connected. - myProject.loop(WiFi.status() == WL_CONNECTED); + if(WiFi.status() == WL_CONNECTED) myProject.loop(); } // **RESULT** -// Prints "Device Connected to Grandeur!\n" when device gets connected to the cloud. -// And prints "Device Disconnected from Grandeur!\n" when device's connection from -// the cloud breaks. +// Prints "Device Connected to Grandeur!\n" when device connects with Grandeur. +// And prints "Device Disconnected from Grandeur!\n" when device's connection with +// Grandeur breaks. ``` ### Fetching Device Variables and Updating Them -On Grandeur, a device has a special space where you can store its variables as key-value pairs, for example, a device's voltage or current or its ON/OFF state. Keeping the online copy of device variables updated gives you many advantages. You can see if your device is ON/OFF just by fetching its `state` from Grandeur. +In IoT, every device in the network wants to publish some variables for users or other devices in the network to see. On Grandeur, we call these the **data** of the device. you can store the device variables as key-value pairs, like the device's voltage or current or its ON/OFF state. Keeping the online copy of device variables (aka. data) updated gives you many advantages: you can see if your device is ON or OFF at any moment just by fetching its `state` from Grandeur. -Both `data().get()` and `data().set()` are **Async functions** because they communicate with Grandeur through internet. Communication through internet takes some time and we cannot wait, for example, for a device variable to arrive from Grandeur โ€” meanwhile blocking the rest of the device program execution. So, what we do is, we schedule a function to be called for the future when the variable arrives and resume with rest of the device program, forgetting that we ever called `data().get()`. When the variable arrives, the SDK calls our scheduled function, giving us access to that variable inside that function. +Both `device(deviceId).data().get()` and `device(deviceId).data().set()` are **Async functions** because they communicate with Grandeur through internet. Communication through internet takes some time (due to Internet latency being in ms while processors run instructions in a fraction of a ยตs) and we cannot wait for our data to arrive from Grandeur, meanwhile blocking the rest of the program execution. So, what we do is, we schedule a function to be called in future when the variable would have arrived and resume the rest of the device program, forgetting that we ever called `device(deviceId).data().get()`. When the variable arrives, the SDK calls our scheduled function automatically, giving us access to that variable inside that function. Read more about **Async functions**, `Callback`, and `Var` [here][the dexterity of arduino sdk]. @@ -254,8 +250,8 @@ Here's how we would get and set device variables: #include #include -Project myProject; -Device myDevice; +Grandeur::Project myProject; +Grandeur::Project::Device myDevice; void setupWiFi(void) { Serial.begin(9600); @@ -268,15 +264,15 @@ void setupWiFi(void) { Serial.printf("\nDevice is connecting to WiFi using SSID %s and Passphrase %s.\n", ssid.c_str(), passphrase.c_str()); } -void getCallback(Var result) { - // This function prints the variables stored in summary and sets the device pins. - Serial.printf("Voltage: %s\n", (int) result["data"]); - analogWrite(A0, (int) result["data"]); +void writeVoltageToA0(const char* code, int voltage) { + // This function prints the voltage value and sets the A0 pin with its value. + Serial.printf("Voltage: %d\n", voltage); + analogWrite(A0, voltage); } -void setCallback(Var result) { - // This function prints the updated values of the variables stored in summary. - Serial.printf("Updated Voltage: %s\n", (int) result["update"]); +void printUpdatedVoltage(const char* code, int voltage) { + // This function prints the updated voltage value after the update is completed. + Serial.printf("Updated Voltage: %d\n", voltage); } void setup() { @@ -289,30 +285,29 @@ void setup() { } void loop() { - if(myProject.isConnected()) { // Getting voltage variable - myDevice.data().get("voltage", getCallback); + myDevice.data().get("voltage", writeVoltageToA0); // Updating voltage int voltage = analogRead(A0); - myDevice.data().set("voltage", voltage, setCallback); + myDevice.data().set("voltage", voltage, printUpdatedVoltage); } // This runs the SDK when the device WiFi is connected. - myProject.loop(WiFi.status() == WL_CONNECTED); + if(WiFi.status() == WL_CONNECTED) myProject.loop(); } // **RESULT** -// When the loop() starts, voltage is fetched. When it arrives from the cloud, getCallback is -// called which prints its value and sets the A0 pin. +// When the loop() starts, voltage is fetched. When it arrives from Grandeur, writeVoltageToA0 is +// called which prints its value and writes it to the A0 pin. // Then the voltage is updated with the new value from the A0 pin. When the update completes, -// setCallback is called with the updated values of voltage which is printed. +// printUpdatedVoltage is called to print the updated value of voltage. ``` ### Handling Updates From Grandeur -You can not only `get()/set()` but also subscribe to a device variable, which means if an update occurs in that variable at any time, you'll instantly get notified of it. -To subscribe to a variable, you just need to pass the variable name and a function to `data().on()`. The function you pass to `data().on()` is set as an **update handlers** for that variable, which means the code inside that function will be run whenever that variable is updated. +You can not only `get()/set()` but also subscribe to a device variable, which means if an update occurs in that variable at any time, the device will instantly get it. +To subscribe to a variable, you just need to pass the variable name and a function to `device(deviceId).data().on()`. The function you pass to `device(deviceId).data().on()` is set as an **update handler** for that variable, which means the code inside that function will be run whenever that variable is updated. Let's set an update handler for device voltage now: @@ -320,8 +315,8 @@ Let's set an update handler for device voltage now: #include #include -Project myProject; -Device myDevice; +Grandeur::Project myProject; +Grandeur::Project::Device myDevice; void setupWiFi(void) { Serial.begin(9600); @@ -334,9 +329,9 @@ void setupWiFi(void) { Serial.printf("\nDevice is connecting to WiFi using SSID %s and Passphrase %s.\n", ssid.c_str(), passphrase.c_str()); } -void voltageUpdatedCallback(int voltage, const char* path) { +void printUpdatedVoltage(const char* path, int voltage) { // This function prints the new value of the voltage variable. - Serial.printf("Updated Voltage: %d\n", voltage); + Serial.printf("Updated Voltage: %d\n", voltage); } void setup() { @@ -346,12 +341,12 @@ void setup() { myProject = grandeur.init(YourApiKey, AccessToken); myDevice = myProject.device(YourDeviceID); - myDevice.data().on("voltage", voltageUpdatedCallback) + myDevice.data().on("voltage", printUpdatedVoltage) } void loop() { // This runs the SDK when the device WiFi is connected. - myProject.loop(WiFi.status() == WL_CONNECTED); + if(WiFi.status() == WL_CONNECTED) myProject.loop(); } // **RESULT** @@ -363,15 +358,15 @@ void loop() { Here we go through a general example to explain the **Arduino SDK** in action. For a little more broken-down approach, do have a look at [these examples][Examples] as well. -To begin working with the **Arduino SDK**, the very first step is to [create a new project][Grandeur Dashboard] and [register a new device][Grandeur Devices] through the [Cloud Dashboard][Grandeur Dashboard]. Then create a new Arduino sketch in your workspace folder. +To begin working with the **Arduino SDK**, you need a [project on Grandeur][Grandeur Dashboard] and a [device registered in that project.][Grandeur Devices]. ### Create a New Sketch -Create a new folder for your `arduino workspace`, create a `.ino` file in it with the same name as the folder, and open it with [Arduino IDE][Arduino IDE]. This is the sketch file where you'll write your device's program. +Create a new folder on your desktop, create a `.ino` file in it with the same name as the folder, and open it with [Arduino IDE][Arduino IDE]. This is the sketch file where you'll write your device's program. -### Include Grandeur.h into Your Sketch +### Include Grandeur.h in Your Sketch -After [cloning the Arduino SDK][installation] and [installing it][Installing an Arduino Library], you can import it into your sketch like this: +After [installing Grandeur in your Arduino IDE][installation], you can import it into your sketch like this: ```cpp #include @@ -379,22 +374,22 @@ After [cloning the Arduino SDK][installation] and [installing it][Installing an ### Initialize the SDK's Configurations -**Arduino SDK** takes care of your device's connection with Grandeur. To use it into your sketch, you need to initialize its configurations first. You can do that using the global object `grandeur`. Initializing the SDK returns a reference to object of the `Project` class which exposes all the SDK's functions. +**Arduino SDK** takes care of your device's connection with Grandeur. To use it into your sketch, you need to initialize its configurations first. You can do that using the global object `grandeur`. Initializing the SDK returns an object of `Grandeur::Project` class which exposes all the SDK's functions. ```cpp #include -Project myProject; -Device myDevice; +Grandeur::Project myProject; +Grandeur::Project::Device myDevice; void setup() { myProject = grandeur.init(YourAPIKey, YourToken); } ``` -You can find the API Key on the [settings page][Grandeur Settings] of your project's dashboard. You get the **Access Token** when you register the device on Grandeur. But a device can only connect to Grandeur if it's paired with a user. And only the paired user has access to the device's data. For convenient testing, we have made device pairing function available on the [devices page][Grandeur Devices] too. You can find your device's ID and pair your device with a user account. If your project has no registered user yet, you can add one easily from the [accounts page][Grandeur Accounts]. +You can find the API Key on the [settings page][Grandeur Settings] of your project's dashboard. You get the **Access Token** when you register the device on Grandeur. But a device can only connect to Grandeur if it's paired with a user. And only a user that's paired with the device has access to the device's data. For convenient testing, we have **Pair Device Button** on the [devices page][Grandeur Devices] too. If your project has no registered users yet, you can add one from the [accounts page][Grandeur Accounts]. -### Handle the device's WiFi +### Handle the Device's WiFi Here we illustrate this with the example of an ESP8266. @@ -402,8 +397,8 @@ Here we illustrate this with the example of an ESP8266. #include #include -Project myProject; -Device myDevice; +Grandeur::Project myProject; +Grandeur::Project::Device myDevice; void setupWiFi(void) { Serial.begin(9600); @@ -426,20 +421,20 @@ void setup() { void loop() { // This runs the SDK when the device WiFi is connected. - myProject.loop(WiFi.status() == WL_CONNECTED); + if(WiFi.status() == WL_CONNECTED) myProject.loop(); } ``` ### Initialize Your Device -Before doing anything, you need to initialize your device with data from Grandeur to keep them both in sync. You can get all the device variables by using `get()` functions. Here's how you can get the device **state** from the cloud. +Before doing anything, you need to sync your device with Grandeur. You can get all the device variables by using `device(deviceId).data().get()` function. Here's a demo. ```cpp #include #include -Project myProject; -Device myDevice; +Grandeur::Project myProject; +Grandeur::Project::Device myDevice; void setupWiFi(void) { Serial.begin(9600); @@ -452,9 +447,9 @@ void setupWiFi(void) { Serial.printf("\nDevice is connecting to WiFi using SSID %s and Passphrase %s.\n", ssid.c_str(), passphrase.c_str()); } -void getStateCallback(Var result) { - if(result["code"] == "DEVICE-DATA-FETCHED") { - bool state = result["data"]; +void initializePins(const char* code, Var variables) { + if(strcmp(code, "DEVICE-DATA-FETCHED") == 0) { + bool state = variables["state"]; // You can set a digital pin here with the state value // to switch the hardware connected to it ON/OFF. digitalWrite(D0, state); @@ -468,28 +463,28 @@ void setup() { myProject = grandeur.init(YourApiKey, YourToken); myDevice = myProject.device(YourDeviceID); - // This gets the device's state variable from Grandeur and passes it to - // getStateCallback() function. - myDevice.data().get("state", getStateCallback); + // This gets all the device variables from Grandeur and passes them to + // initializePins() function. + myDevice.data().get(initializePins); } void loop() { // This runs the SDK when the device WiFi is connected. - myProject.loop(WiFi.status() == WL_CONNECTED); + if(WiFi.status() == WL_CONNECTED) myProject.loop(); } ``` ### Set Update Handlers -Update handlers are the functions which are called when a device variable is updated. The update could be from a user or the device itself. Without the handlers, your device would not be notified when a user turns it off from the web app. -Here's how you can set an update handler in your sketch for the device's state. +Update handlers are the functions which are called when a device variable is updated. The device variable could have been updated by the paired user or the device itself. Without the handlers, your device would not know and handle it when its user turns it off from the web app. +Here's how you can set an update handler in your sketch for the device's `state`. ```cpp #include #include -Project myProject; -Device myDevice; +Grandeur::Project myProject; +Grandeur::Project::Device myDevice; void setupWiFi(void) { Serial.begin(9600); @@ -502,16 +497,16 @@ void setupWiFi(void) { Serial.printf("\nDevice is connecting to WiFi using SSID %s and Passphrase %s.\n", ssid.c_str(), passphrase.c_str()); } -void getStateCallback(Var result) { - if(result["code"] == "DEVICE-DATA-FETCHED") { - bool state = result["data"]; +void initializePins(const char* code, Var variables) { + if(strcmp(code, "DEVICE-DATA-FETCHED") == 0) { + bool state = variables["state"]; // You can set a digital pin here with the state value // to switch the hardware connected to it ON/OFF. digitalWrite(D0, state); } } -void stateUpdatedCallback(bool state, const char* path) { +void writeToD0(const char* path, bool state) { // You can set a digital pin here with the newState value // to switch the hardware connected to it ON/OFF. digitalWrite(D0, state); @@ -524,30 +519,30 @@ void setup() { myProject = grandeur.init(YourApiKey, YourToken); myDevice = myProject.device(YourDeviceID); - // This gets the device's state variable from Grandeur and passes it to - // getStateCallback() function. - myDevice.data().get("state", getStateCallback); + // This gets all the device variables from Grandeur and passes them to + // initializePins() function. + myDevice.data().get(initializePins); // This sets up the update handler for state. When an update to state occurs on Grandeur, - // stateUpdatedCallback() function is called. - myDevice.data().on("state", stateUpdatedCallback); + // writeToD0 function is called. + myDevice.data().on("state", writeToD0); } void loop() { // This runs the SDK when the device WiFi is connected. - myProject.loop(WiFi.status() == WL_CONNECTED); + if(WiFi.status() == WL_CONNECTED) myProject.loop(); } ``` ### Update Device Variables -To see the live state of the device on the web app, you need to keep sending the updated state after every few seconds. We'll use the `set()` function to update the state value. +To get the live stream of a device variable on the user-end, you need to keep sending the updated state after every few seconds. We'll use the `device(deviceId).data().set()` function to update the state value. ```cpp #include #include -Project myProject; -Device myDevice; +Grandeur::Project myProject; +Grandeur::Project::Device myDevice; void setupWiFi(void) { Serial.begin(9600); @@ -560,24 +555,24 @@ void setupWiFi(void) { Serial.printf("\nDevice is connecting to WiFi using SSID %s and Passphrase %s.\n", ssid.c_str(), passphrase.c_str()); } -void getStateCallback(Var result) { - if(result["code"] == "DEVICE-DATA-FETCHED") { - bool state = result["data"]; +void initializePins(const char* code, Var variables) { + if(strcmp(code, "DEVICE-DATA-FETCHED") == 0) { + bool state = variables["state"]; // You can set a digital pin here with the state value // to switch the hardware connected to it ON/OFF. digitalWrite(D0, state); } } -void stateUpdatedCallback(bool state, const char* path) { +void writeToD0(const char* path, bool state) { // You can set a digital pin here with the newState value // to switch the hardware connected to it ON/OFF. digitalWrite(D0, state); } -void setStateCallback(Var result) { - if(result["code"] == "DEVICE-DATA-UPDATED") { - Serial.printf("State is updated to: %d\n", (bool) result["update"]); +void printUpdatedState(const char* code, bool state) { + if(strcmp(code, "DEVICE-DATA-UPDATED") == 0) { + Serial.printf("State is updated to: %d\n", state); } } @@ -588,53 +583,55 @@ void setup() { myProject = grandeur.init(YourApiKey, YourToken); myDevice = myProject.device(YourDeviceID); - // This gets the device's state variable from Grandeur and passes it to - // getStateCallback() function. - myDevice.data().get("state", getStateCallback); + // This gets all the device variables from Grandeur and passes them to + // initializePins() function. + myDevice.data().get(initializePins); // This sets up the update handler for state. When an update to state occurs on Grandeur, - // stateUpdatedCallback() function is called. - myDevice.data().on("state", stateUpdatedCallback); + // writeToD0 function is called. + myDevice.data().on("state", writeToD0); } void loop() { bool state = digitalRead(D0); - // This sends the updated state to Grandeur and calls setStateCallback() when - // Grandeur acknowledges the update. - myDevice.data().set("state", state, setStateCallback); + // This sends the updated state to Grandeur and calls printUpdatedState() after + // the update completes. + myDevice.data().set("state", state, printUpdatedState); // This runs the SDK when the device WiFi is connected. - myProject.loop(WiFi.status() == WL_CONNECTED); + if(WiFi.status() == WL_CONNECTED) myProject.loop(); } ``` ### Test it With Your Web app -You can build a web app for your product to control your hardware device over the cloud. [Here's a simple example for that][An Example Webapp]. +You can build a web app to control this device over Grandeur. [Here's a simple example for that][An Example Webapp]. ## The Dexterity of Arduino SDK The Arduino SDK is aimed at providing extremely to-the-point functions, being almost invisible in your device program to make the integration of Grandeur in your product seamless. Here is what it does under the hood without you paying attention to the most painful things: -* **Arduino SDK** takes care of your device's connection to [Grandeur][Grandeur]. **It can start trying to connect with Grandeur as soon as the device boots or you can manually tell the SDK when to begin.** There's a [`loop()`][loop] function that you place in the Arduino's `loop` whose sole function is to run the SDK. It accepts a **boolean expression as argument** and the SDK runs when the boolean expression evaluates to `true`. So, let's say if you pass the expression `WiFiState == CONNECTED` to it, the SDK would only run when the device's WiFi is connected. +* **Arduino SDK** takes care of your device's connection to [Grandeur][Grandeur]. **It can start trying to connect with Grandeur as soon as the device boots or you can manually tell the SDK when to begin.** There's [`Grandeur::Project::loop`][loop] function that you place in the Arduino's `loop` whose sole function is to run the SDK. It accepts a **boolean expression as argument** and the SDK runs when the boolean expression evaluates to `true`. So, let's say if you pass the expression `WiFiState == CONNECTED` to it, the SDK would only run when the device's WiFi is connected. Or you can also wrap the `Grandeur::Project::loop` in an if-statement. -* As soon as the WiFi gets connected, **Arduino SDK** begins trying to connect to *[Grandeur][Grandeur]* using the **connection credentials** you provide during `grandeur.init()`. When it connects, only then does the communication with Grandeur happen. And if somehow the connection breaks, SDK handles the reconnection and everything resumes right from where it left. +* As soon as the WiFi gets connected, **Arduino SDK** begins trying to connect to **[Grandeur][Grandeur]** using the **credentials** you provide during `grandeur.init()`. When it connects, only then does the communication with Grandeur happen. And if somehow the connection breaks, SDK handles the reconnection and everything resumes right from where it left. -* **Arduino SDK** exposes the state of your device (`CONNECTED` or `DISCONNECTED`) through [`getState()`][getState] function to let you make your decisions based on that. +* **Arduino SDK** maintains a network buffer. So if, for some reason, your device disconnects from the internet, the SDK buffers the messages during the offline period and flushes them in sequence when the device is back online. -* **Arduino SDK** is event-driven. You can set **event handler** for device's connection or disconnection with Grandeur by using [`onConnection()`][onConnection]. So, when the device connects or disconnects from the cloud, the function passed to `onConnection()` is called. +* **Arduino SDK** exposes the state of your device (`CONNECTED` or `DISCONNECTED`) through [`Grandeur::Project::getState`][getState] function to let you make your decisions based on that. -* You can also set **update handlers** for device variables using [`data().on()`][on] function. So, when any of the device variables is updated, the corresponding function passed to `on()` is called. +* **Arduino SDK** is event-driven. You can set **event handler** for device's connection or disconnection with Grandeur by using [`Grandeur::Project::onConnection`][onConnection]. So, when the device connects or disconnects with Grandeur, the function you passed to `onConnection()` is called. + +* You can also set **update handlers** for device variables using [`Grandeur::Project::Device::Data::on`][on] function. So, when any of the device variables is updated, the corresponding function you passed to `Grandeur::Project::Device::Data::on` is called. * **Async functions** are what make the event-drive of the SDK possible. They do all the same things as regular functions plus one extra. They receive a function parameter which they schedule for later. For example, all of the following are Async functions: - * `project.onConnection(Callback callback)` - * `project.device().data().on(String path, function callback)` - * `project.device().data().get(String path, function callback)` - * `project.device().data().set(String path, Var data, function callback)` + * `Grandeur::Project::onConnection(Callback callback)` + * `Grandeur::Project::Device::Data::on(String path, function callback)` + * `Grandeur::Project::Device::Data::get(String path, function callback)` + * `Grandeur::Project::Device::Data::set(String path, Var data, function callback)` - `get()` for example, requests Grandeur for a device variable and schedules the `callback` function for when the variable arrives, because obviously, they don't arrive instantaneously; there is always some latency involved in web communications. + `Grandeur::Project::Device::Data::get` for example, requests Grandeur for a device variable and schedules the `callback` function for when the variable arrives, because obviously, they don't arrive instantaneously; there is always some latency involved in web communications. -* [`Var`][var] is a special variable type which can take form of any other type including int, double, String, etc, like in those dynamic typed languages like javascript. But it can also act like a container for other variables, like a javascript object or a C++ map, to form JSON. You can store variables in it as key-value pairs. This is device data space is โ€” a container for device variables aka. `Var`. +* [`Var`][var] is a special variable type which can take form of any other type including int, double, String, bool, etc, like in those dynamic typed languages like javascript. But it can also act like a container for other variables, like a javascript object or a C++ map, to form JSON. You can store variables in it as key-value pairs. ```cpp Var json; // The root @@ -844,7 +841,7 @@ void loop() { Serial.println("Yay! Device has made a successful connection with Grandeur!\n"); } - myProject.loop(true); + myProject.loop(); } // **RESULT** @@ -855,7 +852,7 @@ void loop() { ### onConnection -> onConnection (callback : _Callback_) : returns _void_ +> onConnection (callback : _void (*)(bool)_) : returns _void_ This method schedules a function to be called when the device's connection with Grandeur is made or broken. The function passed to it as argument is called an **event handler** for it handles events like connection/disconnection with Grandeur. Example below illustrates its usage. @@ -902,10 +899,11 @@ void loop() { ### loop +> loop () : returns _void_ > loop (valve: _bool_) : returns _void_ This method is the legs of the SDK. Without it, the SDK doesn't run. Therefore, it must be called in Arduino's `loop()` and without being suspected to any *delay*. **This method is what runs the underlying event loop and makes all the *Async* functions possible.** -It can also accept an argument which we call **valve**. A **valve** is a boolean expression whose value decides if the SDK would run for current `loop` or not. For example, we can use it to dictate to the SDK to run only when the device WiFi is connected. +It can also accept an argument which we call **valve**. A **valve** is a boolean expression whose value decides if the SDK would run for current `loop` or not. For example, we can use it to dictate to the SDK to run only when the device WiFi is connected. You can also use an if-statement instead. > **A Tidbit:** [Here][Using Millis Instead of Delay] is how you can use `millis()` instead of `delay()` if you want a function to run after every few moments without blocking the loop. @@ -1009,7 +1007,7 @@ This method gets a device variable from Grandeur. | Name | Type | Description | |-------------|------------|--------------------------------------------------------------| -| path | _String_ | Path of the device variable using dot notation | +| path | _String_ | Path of the device variable using dot notation | | callback | _Callback_ | A function to be called when get response is received | #### Example @@ -1018,9 +1016,9 @@ This method gets a device variable from Grandeur. Grandeur::Project myProject; Grandeur::Project::Device myDevice; -void getVoltageCallback(Var result) { +void getVoltageCallback(const char* code, int voltage) { // You can write any type int/double/bool/const char* in place of Var and it'll cast voltage to that type. // This method prints *voltage* variable from device data. - Serial.println(result["data"]<<"\n"); + Serial.println(voltage); } void setup() { @@ -1063,9 +1061,9 @@ This method updates a device variable on Grandeur with new data. Grandeur::Project myProject; Grandeur::Project::Device myDevice; -void setVoltageCallback(Var result) { +void setVoltageCallback(const char* code, int voltage) { // You can write any type int/double/bool/const char* in place of int and it'll cast voltage to that type. // This method prints "voltage" value after it is updated on Grandeur. - Serial.println(result["update"]); + Serial.println(voltage); } void setup() { @@ -1116,11 +1114,10 @@ More on Callback [here][callback]. Grandeur::Project myProject; Grandeur::Project::Device myDevice; -void voltageUpdatedCallback(Var result) { - // When "voltage" update occurs on Grandeur, this function extracts - // its updated value and writes it to an analog pin. +void voltageUpdatedCallback(const char* path, int voltage) { + // When "voltage" update occurs on Grandeur, this function + // writes it to an analog pin. Serial.println("Voltage update occurred!\n"); - int voltage = result["voltage"]; analogWrite(voltage, A0); }