diff --git a/README.md b/README.md index ed98719..0cf0285 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,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 defined 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 initialize the SDK's configurations. ```cpp #include @@ -109,7 +109,7 @@ When you include `` in your sketch, a global object `grandeur` is de ### Initialization -Initialization is as simple as calling `grandeur.init()` with your credentials (Device ID, 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 (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. ```cpp #include @@ -118,7 +118,7 @@ Project myProject; void setup() { // You can initialize device configurations like this. - myProject = grandeur.init(YourDeviceID, YourDeviceToken); + myProject = grandeur.init(YourAPIKey, AccessToken); } void loop() {} @@ -161,7 +161,7 @@ void setup() { // This sets up the device WiFi. setupWiFi(); // You can initialize device configurations like this. - myProject = grandeur.init(YourApiKey, YourDeviceToken); + myProject = grandeur.init(YourApiKey, AccessToken); } void loop() { @@ -181,7 +181,7 @@ You can see this line in the previous subsection: `myProject.loop(WiFi.status() 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 `JSONObject` as a parameter and returns `void`. Read more about `Callback` and `JSONObject` [here][jsonobject]. +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]. Here's how you can handle the connection event: @@ -239,20 +239,13 @@ void loop() { ### Fetching Device Variables and Updating Them -On Grandeur, we generally store the device data in two containers: **summary** to contain uncontrollable device variables and **parms** to contain controllable device variables. You can get and set both types using the following functions of the `Device` class: - -* `myDevice.getSummary()` -* `myDevice.getParms()` -* `myDevice.setSummary()` -* `myDevice.setParms()` +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. -They are all **Async functions** because they communicate with Grandeur through internet. Communication through internet takes some time and we cannot wait, for example, for device's summary variables to arrive from Grandeur -- meanwhile blocking the rest of the device program. So, what we do is, we schedule a function to be called when the summary variables and resume with rest of the device program, forgetting that we ever called `getSummary()`. When the summary variables arrive, the SDK calls our scheduled function, giving us access to summary variables inside that function. +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. -For now, there's only one type of function that the SDK's Async methods accept: `Callback` which accepts a `JSONObject` as argument and returns nothing aka. `void`. +Read more about **Async functions**, `Callback`, and `Var` [here][the dexterity of arduino sdk]. -Read more about **Async functions**, `Callback`, and `JSONObject` [here][the dexterity of arduino sdk]. - -Here's how we would get and set device's summary and parms: +Here's how we would get and set device variables: ```cpp #include @@ -272,29 +265,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 getSummaryCallback(JSONObject result) { +void getCallback(Var result) { // This function prints the variables stored in summary and sets the device pins. - Serial.printf("Voltage: %s\n", (int) result["deviceSummary"]["voltage"]); - Serial.printf("Current: %s\n", (int) result["deviceSummary"]["current"]); - analogWrite(A0, (int) result["deviceSummary"]["voltage"]); - analogWrite(A1, (int) result["deviceSummary"]["current"]); + Serial.printf("Voltage: %s\n", (int) result["data"]); + analogWrite(A0, (int) result["data"]); } -void getParmsCallback(JSONObject result) { - // This function prints the variables stored in parms and sets device pins. - Serial.printf("State: %s\n", (bool) result["deviceParms"]["state"]); - digitalWrite(D0, (bool) result["deviceParms"]["state"]); -} - -void setSummaryCallback(JSONObject result) { +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"]["voltage"]); - Serial.printf("Updated Current: %s\n", (int) result["update"]["current"]); -} - -void setParmsCallback(JSONObject result) { - // This function prints the updated values of the variables stored in parms. - Serial.printf("Updated State: %s\n", (bool) result["update"]["state"]); + Serial.printf("Updated Voltage: %s\n", (int) result["update"]); } void setup() { @@ -302,26 +281,18 @@ void setup() { // This sets up the device WiFi. setupWiFi(); // You can initialize device configurations like this. - myProject = grandeur.init(YourApiKey, YourDeviceToken); + myProject = grandeur.init(YourApiKey, AccessToken); myDevice = myProject.device(YourDeviceID); } void loop() { if(myProject.isConnected()) { - // Getting device's summary - myDevice.getSummary(getSummaryCallback); - // Getting device's parms - myDevice.getParms(getParmsCallback); - // Updating device's summary - JSONObject summary; - summary["voltage"] = analogRead(A0); - summary["current"] = analogRead(A1); - myDevice.setSummary(summary, setSummaryCallback); - // Updating device's parms - JSONObject parms; - parms["state"] = digitalRead(D0); - myDevice.setParms(parms, setParmsCallback); + // Getting voltage variable + myDevice.data().get("voltage", getCallback); + // Updating voltage + int voltage = analogRead(A0); + myDevice.data().set("voltage", voltage, setCallback); } // This runs the SDK when the device WiFi is connected. @@ -329,17 +300,18 @@ void loop() { } // **RESULT** -// When the loop() starts, summary and parms are fetched. When they arrive from the cloud, their -// corresponding callbacks are called which print the variables stored in summary and parms objects -// and set the corresponding pins. -// Then the summary and parms are updated with the new values. When their updates complete, their -// callbacks are called with the updated values of their variables and these updated values are -// printed on the screen. +// 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. +// 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. ``` ### Handling Updates From Grandeur -Device variables are distributed on the cloud in form of **summary** and **parms**. Passing a `Callback` to `onSummary()` and `onParms()` you can set **update handlers** for updates to those variables. Let's do that now: +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. + +Let's set an update handler for device voltage now: ```cpp #include @@ -359,26 +331,19 @@ void setupWiFi(void) { Serial.printf("\nDevice is connecting to WiFi using SSID %s and Passphrase %s.\n", ssid.c_str(), passphrase.c_str()); } -void summaryUpdatedCallback(JSONObject result) { - // This function prints the updated values of the variables stored in summary. - Serial.printf("Updated Voltage: %s\n", (int) updatedSummary["voltage"]); - Serial.printf("Updated Current: %s\n", (int) updatedSummary["current"]); -} - -void parmsUpdatedCallback(JSONObject result) { - // This function prints the updated values of the variables stored in parms. - Serial.printf("Updated State: %s\n", (int) updatedParms["state"]); +void voltageUpdatedCallback(int voltage, const char* path) { + // This function prints the new value of the voltage variable. + Serial.printf("Updated Voltage: %d\n", voltage); } void setup() { Serial.begin(9600); setupWiFi(); - myProject = grandeur.init(YourApiKey, YourDeviceToken); + myProject = grandeur.init(YourApiKey, AccessToken); myDevice = myProject.device(YourDeviceID); - myDevice.onSummary(summaryUpdatedCallback); - myDevice.onParms(parmsUpdatedCallback); + myDevice.data().on("voltage", voltageUpdatedCallback) } void loop() { @@ -387,8 +352,8 @@ void loop() { } // **RESULT** -// Whenever an update in the device's summary or parms occur, the updated values of the -// variables are printed. +// Whenever an update in the device's voltage variable occurs, the new value of the +// voltage is printed to Serial. ``` ## Example @@ -399,7 +364,7 @@ To begin working with the **Arduino SDK**, the very first step is to [create a n ### Create a New Sketch -Create a new folder for your `arduino workspace`, create a `.ino` file in it, 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 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. ### Include Grandeur.h into Your Sketch @@ -411,7 +376,7 @@ 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 an object of `GrandeurDevice` 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 a reference to object of the `Project` class which exposes all the SDK's functions. ```cpp #include @@ -424,7 +389,7 @@ void setup() { } ``` -You can find the API Key on the [settings page][Grandeur Settings] of your project's dashboard. For the Access Token, you need to pair your device with a user account in your project first. A device can only connect to Grandeur if it's paired with a user. And only the paired user can access the device's data through its web app. 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 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]. ### Handle the device's WiFi @@ -453,7 +418,7 @@ void setup() { // This sets up the device WiFi. setupWiFi(); // You can initialize device configurations like this. - myProject = grandeur.init(YourApiKey, YourDeviceToken); + myProject = grandeur.init(YourApiKey, YourToken); } void loop() { @@ -464,7 +429,7 @@ void loop() { ### Initialize Your Device -Before doing anything, you need to initialize your device with data from the cloud to keep them both in sync. You can get all the device variables by using `getSummary()` and `getParms()` functions. Here's how you can get the device **state** from the cloud. +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. ```cpp #include @@ -484,9 +449,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 getParmsCallback(JSONObject parms) { - if(payload["code"] == "DEVICE-PARMS-FETCHED") { - bool state = (bool) payload["deviceParms"]["state"]; +void getStateCallback(Var result) { + if(result["code"] == "DEVICE-DATA-FETCHED") { + bool state = result["data"]; // You can set a digital pin here with the state value // to switch the hardware connected to it ON/OFF. digitalWrite(D0, state); @@ -498,11 +463,11 @@ void setup() { // This sets up the device WiFi. setupWiFi(); - myProject = grandeur.init(YourApiKey, YourDeviceToken); + myProject = grandeur.init(YourApiKey, YourToken); myDevice = myProject.device(YourDeviceID); - // This gets the device's parms variables from Grandeur and passes them to - // getParmsCallback() function. - myDevice.getParms(getParmsCallback); + // This gets the device's state variable from Grandeur and passes it to + // getStateCallback() function. + myDevice.data().get("state", getStateCallback); } void loop() { @@ -513,8 +478,8 @@ void loop() { ### Set Update Handlers -Update handlers are the functions which are called when a device variable is updated on the cloud. 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 webapp. -Here's how you can set update handlers in your sketch for the device's state stored in parms. +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. ```cpp #include @@ -534,20 +499,19 @@ void setupWiFi(void) { Serial.printf("\nDevice is connecting to WiFi using SSID %s and Passphrase %s.\n", ssid.c_str(), passphrase.c_str()); } -void getParmsCallback(JSONObject parms) { - if(payload["code"] == "DEVICE-PARMS-FETCHED") { - bool state = (bool) payload["deviceParms"]["state"]; +void getStateCallback(Var result) { + if(result["code"] == "DEVICE-DATA-FETCHED") { + bool state = result["data"]; // You can set a digital pin here with the state value // to switch the hardware connected to it ON/OFF. digitalWrite(D0, state); } } -void parmsUpdatedCallback(JSONObject parms) { - bool newState = (bool) updatedParms["state"]; +void stateUpdatedCallback(bool state, const char* path) { // You can set a digital pin here with the newState value // to switch the hardware connected to it ON/OFF. - digitalWrite(D0, newState); + digitalWrite(D0, state); } void setup() { @@ -555,14 +519,14 @@ void setup() { // This sets up the device WiFi. setupWiFi(); - myProject = grandeur.init(YourApiKey, YourDeviceToken); + myProject = grandeur.init(YourApiKey, YourToken); myDevice = myProject.device(YourDeviceID); - // This gets the device's parms variables from Grandeur and passes them to - // getParmsCallback() function. - myDevice.getParms(getParmsCallback); - // This sets up the update handler for parms. When an update to parms occur on Grandeur, - // parmsUpdatedCallback() function is called. - myDevice.onParmsUpdated(parmsUpdatedCallback); + // This gets the device's state variable from Grandeur and passes it to + // getStateCallback() function. + myDevice.data().get("state", getStateCallback); + // 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); } void loop() { @@ -573,7 +537,7 @@ void 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. Since we've stored the device's state in **Parms**, we'll use the `setParms()` function to update the state value. +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. ```cpp #include @@ -593,25 +557,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 getParmsCallback(JSONObject parms) { - if(payload["code"] == "DEVICE-PARMS-FETCHED") { - bool state = (bool) payload["deviceParms"]["state"]; +void getStateCallback(Var result) { + if(result["code"] == "DEVICE-DATA-FETCHED") { + bool state = result["data"]; // You can set a digital pin here with the state value // to switch the hardware connected to it ON/OFF. digitalWrite(D0, state); } } -void parmsUpdatedCallback(JSONObject parms) { - bool newState = (bool) updatedParms["state"]; +void stateUpdatedCallback(bool state, const char* path) { // You can set a digital pin here with the newState value // to switch the hardware connected to it ON/OFF. - digitalWrite(D0, newState); + digitalWrite(D0, state); } -void setParmsCallback(JSONObject parms) { - if(result["code"] == "DEVICE-PARMS-UPDATED") { - Serial.printf("State is updated to: %d\n", (bool) payload["update"]["state"]); +void setStateCallback(Var result) { + if(result["code"] == "DEVICE-DATA-UPDATED") { + Serial.printf("State is updated to: %d\n", (bool) result["update"]); } } @@ -620,23 +583,21 @@ void setup() { // This sets up the device WiFi. setupWiFi(); - myProject = grandeur.init(YourApiKey, YourDeviceToken); + myProject = grandeur.init(YourApiKey, YourToken); myDevice = myProject.device(YourDeviceID); - // This gets the device's parms variables from Grandeur and passes them to - // getParmsCallback() function. - myDevice.getParms(getParmsCallback); - // This sets up the update handler for parms. When an update to parms occur on Grandeur, - // parmsUpdatedCallback() function is called. - myDevice.onParmsUpdated(parmsUpdatedCallback); + // This gets the device's state variable from Grandeur and passes it to + // getStateCallback() function. + myDevice.data().get("state", getStateCallback); + // 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); } void loop() { - // Parms container to store device's state. - JSONObject parms; - parms["state"] = digitalRead(D0); - // This sends the updated parms to Grandeur and calls setParmsCallback() when - // the response from the cloud arrives. - myDevice.setParms(parms, setParmsCallback); + 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 runs the SDK when the device WiFi is connected. myProject.loop(WiFi.status() == WL_CONNECTED); @@ -659,35 +620,26 @@ The Arduino SDK is aimed at providing extremely to-the-point functions, being al * **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. -* You can also set **update handlers** for device's summary and parms using [`onSummary()`][onSummary] and [`onParms()`][onParms]. So, when the any of the device variables stored in summary or parms is updated, the function passed to `onSummary()` or `onParmsUpdated()` is called. +* 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. -* **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, in the `GrandeurDevice` class, all of the following are Async functions: +* **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: - * `onConnection(Callback callback)` - * `onSummary(Callback callback)` - * `onParmsUpdated(Callback callback)` - * `getSummary(Callback callback)` - * `getParms(Callback callback)` - * `setSummary(JSONObject summary, Callback callback)` - * `setParms(JSONObject parms, Callback callback)` + * `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)` - `getParms()` for example, requests the cloud for the device's parms and schedules the `callback` function for when the parms arrive, because obviously, they don't arrive instantaneously; there is always some latency involved in web communications. + `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. -* There is a special type of function defined in **Arduino SDK** as [`Callback`][callback]. It's nothing but a regular function of the form: - ```cpp - void callback(JSONObject result) {} - ``` - For now, it is the only function type that the Async functions of SDK accept as argument. - -* [`JSONObject`][jsonobject] is a special variable type which acts like a container for other variables, just like a javascript object or a C++ map. You can store variables in it as key-value pairs. This what summary and parms are -- container for other variables aka. `JSONObject`s. +* [`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`. ```cpp -JSONObject summary; -summary["foo"] = "This is foo"; -summary["bar"] = true; -summary["foobar"]["foo"] = "This is foo inside foobar"; +Var json; // The root +json["foo"] = "This is foo"; // "foo" key in json +json["bar"] = true; // "bar" key in json +json["foobar"]["foo"] = "This is foo inside foobar"; // "foo" key in "foobar" key in json -// In JSON, summary would look like: +// In JSON, json would look like: // { // "foo": "This is foo", // "bar": true, @@ -695,6 +647,12 @@ summary["foobar"]["foo"] = "This is foo inside foobar"; // "foo": "This is foo inside foobar" // } // } + +// You can also store anything in Var. +Var x = 5; +x = "hello"; + +Serial.println(x); // Prints: hello ``` To see the **Arduino SDK** in action, jump to [Example][Example]. @@ -819,17 +777,17 @@ 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 get the object of this class when you initialize SDK's configurations using `grandeur.init()`. You can safely imagine the object of `Project` class as a reference to your project on Grandeur. +`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 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()`. -> ***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 them to work. You can see [these examples][Examples] to get a deeper understanding. They show 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 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 (deviceID: _String_, apiKey: _String_, token: _String_) : returns _Project_ +> grandeur.init (apiKey: _String_, token: _String_) : returns _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 of the SDK. +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. #### Parameters @@ -854,7 +812,7 @@ void setup() { ## Project -Project is the main class of the SDK. When SDK connects with Grandeur, this class represents your cloud project. But Arduino SDK's functionality is limited to devices and datastore APIs which are wrapped by their respective classes. +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. This class exposes the following methods: @@ -899,7 +857,7 @@ This method schedules a function to be called when the SDK's connection with Gra | 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 @@ -964,7 +922,7 @@ void loop() { > device (deviceID: _String_) : returns _Device_ -This method returns an object of the **Device** class. Read about **Device** class [here][Device Class]. +This method returns a reference to object of the **Device** class. Read about **Device** class [here][Device Class]. #### Example @@ -984,7 +942,7 @@ void setup() { > datastore (void) : returns _Datastore_ -This method returns an 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. +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. #### Example @@ -1002,21 +960,22 @@ void setup() { ## Device -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. +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. -It exposes the following functions: +Device's `Data` class exposes the following functions: -### getSummary +### get -> getSummary (callback: _Callback_) : returns _void_ +> get (path: _String_, callback: _Callback_) : returns _void_ -This method gets device's [summary][summary] from Grandeur. +This method gets a device variable from Grandeur. #### Parameters | Name | Type | Description | |-------------|------------|--------------------------------------------------------------| -| callback | _Callback_ | A function to be called when getSummary response is received | +| path | _String_ | Path of the device variable using dot notation | +| callback | _Callback_ | A function to be called when get response is received | #### Example @@ -1024,9 +983,9 @@ This method gets device's [summary][summary] from Grandeur. Project myProject; Device myDevice; -void getSummaryCallback(JSONObject result) { +void getVoltageCallback(Var result) { // This method just prints *voltage* variable from the device's summary. - Serial.println(result["summary"]["voltage"]<<"\n"); + Serial.println(result["data"]<<"\n"); } void setup() { @@ -1037,67 +996,28 @@ void setup() { void loop() { // This gets the summary on every loop and calls getSummaryCallback() function when its // response from the cloud is received. - myDevice.getSummary(getSummaryCallback); - - myProject.loop(true); -} - -// **RESULT** -// Prints the value of the voltage variable stored in the device's summary on every loop. -``` - -### getParms - -> getParms (callback: _Callback_) : returns _void_ - -This method gets device's [parms][parms] from Grandeur. - -#### Parameters - -| Name | Type | Description | -|-------------|------------|------------------------------------------------------------| -| callback | _Callback_ | A function to be called when getParms response is received | - -#### Example - -```cpp -Project myProject; -Device myDevice; - -void getParmsCallback(JSONObject result) { - // This method just prints *state* variable from the device's parms. - Serial.println(result["parms"]["state"]<<"\n"); -} - -void setup() { - myProject = grandeur.init(YourApiKey, YourToken); - myDevice = myProject.device(YourDeviceID); -} - -void loop() { - // This gets the parms on every loop and calls getParmsCallback() function when its - // response from the cloud is received. - myDevice.getParms(getParmsCallback); + myDevice.data().get("voltage", getVoltageCallback); myProject.loop(true); } // **RESULT** -// Prints the value of the state variable stored in the device's parms on every loop. +// Prints the value of the voltage variable from Grandeur on every loop. ``` -### setSummary +### set -> setSummary (summary : _JSONObject_, callback: _Callback_) : returns _void_ +> set (path : _String_, data : _any_, callback: _Callback_) : returns _void_ -This method updates the device's [summary][summary] on Grandeur with new values. +This method updates a device variable on Grandeur with new data. #### Parameters | Name | Type | Description | |-------------|---------------|--------------------------------------------------------------| -| summary | _JSONObject_ | A JSONObject containing the summary variables | -| callback | _Callback_ | A function to be called when setSummary response is received | +| 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 | #### Example @@ -1105,10 +1025,9 @@ This method updates the device's [summary][summary] on Grandeur with new values. Project myProject; Device myDevice; -void setSummaryCallback(JSONObject result) { - // This method prints *voltage* and *current* variables from device's updated summary. - Serial.println(result["update"]["voltage"]<<"\n"); - Serial.println(result["update"]["current"]<<"\n"); +void setVoltageCallback(Var result) { + // This method prints *voltage* value after it is updated on Grandeur. + Serial.println(result["update"]); } void setup() { @@ -1117,13 +1036,11 @@ void setup() { } void loop() { - // A JSONObject container to store summary variables. - JSONObject summary; - summary["voltage"] = analogRead(A0); - summary["current"] = analogRead(A1); + // 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. - myDevice.setSummary(summary, setSummaryCallback); + myDevice.data().set("voltage", voltage, setVoltageCallback); myProject.loop(true); } @@ -1133,64 +1050,20 @@ void loop() { // variables (voltage and current in our case) on every loop. ``` -### setParms +### on -> setParms (parms : _JSONObject_, callback: _Callback_) : returns _void_ +> on (path: _String_, callback : _Callback_) : returns _void_ -Setter method for device's [parms][parms]. - -#### Parameters - -| Name | Type | Description | -|-------------|--------------|-----------------------------------------------------------------| -| parms | _JSONObject_ | A JSONObject containing the summary variables | -| callback | _Callback_ | A function to be called when setParms response is received | - -#### Example - -```cpp -Project myProject; -Device myDevice; - -void setParmsCallback(JSONObject result) { - // This method prints *state* variable from device's updated parms. - Serial.println(result["parms"]["state"]<<"\n"); -} - -void setup() { - myProject = grandeur.init(YourApiKey, YourToken); - myDevice = myProject.device(YourDeviceID); -} - -void loop() { - // A JSONObject container to store parms variables. - JSONObject parms; - parms["state"] = digitalRead(D0); - // This sets the parms on every loop and calls setParmsCallback() function when its - // response from the cloud is received. - myDevice.setParms(parms, setParmsCallback); - - myProject.loop(true); -} - -// **RESULT** -// Setts the parms and prints the updated values of the parms -// variables (just state in our case) on every loop -``` - -### onSummary - -> onSummary (callback : _Callback_) : returns _void_ - -This method schedules a function to be called when the summary of the device is updated on Grandeur. +This method schedules a function to be called when a device variable changes on Grandeur. > ***A Tidbit***: *Update is a special type of event* and the function that handles it is called an **update handler**. #### Parameters -| Name | Type | Description | -|-------------|------------|----------------------------------------| -| callback | _Callback_ | An update handler for device's summary | +| Name | Type | Description | +|-------------|------------|------------------------------------------------| +| path | _String_ | Path of the device variable using dot notation | +| callback | _Callback_ | An update handler for the device variable | More on Callback [here][callback]. @@ -1200,60 +1073,19 @@ More on Callback [here][callback]. Project myProject; Device myDevice; -void summaryUpdatedCallback(JSONObject result) { +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"]; - int current = updatedSummary["current"]; digitalWrite(voltage, A0); - digitalWrite(current, A1); } void setup() { myProject = grandeur.init(YourApiKey, YourToken); myDevice = myProject.device(YourDeviceID); - myDevice.onSummary(summaryUpdatedCallback); -} - -void loop() { - myProject.loop(true); -} -``` - -### onParms - -> onParms (callback : _Callback_) : returns _void_ - -This method schedules a function to be called when the parms of the device are updated on Grandeur. - -#### Parameters - -| Name | Type | Description | -|-------------|------------|--------------------------------------| -| callback | _Callback_ | An update handler for device's parms | - -More on Callback [here][callback]. - -#### Example - -```cpp -Project myProject; -Device myDevice; - -void parmsUpdatedCallback(JSONObject updatedParms) { - // When parms update occurs on Grandeur, this function extracts the updated value of - // state, and sets the corresponding pin. - Serial.println("Parms update occurred!\n"); - int state = updatedParms["state"]; - digitalWrite(D0, state); -} - -void setup() { - myProject = grandeur.init(YourApiKey, YourToken); - myDevice = myProject.device(YourDeviceID); - myDevice.onParms(parmsUpdatedCallback); + myDevice.on("voltage", voltageUpdatedCallback); } void loop() { @@ -1269,7 +1101,7 @@ It exposes the following functions: ### insert -> insert (documents: _JSONObject_, callback: _Callback_) : returns _void_ +> insert (documents: _Var_, callback: _Callback_) : returns _void_ This method inserts documents into Grandeur datastore. @@ -1277,7 +1109,7 @@ This method inserts documents into Grandeur datastore. | Name | Type | Description | |-------------|--------------|--------------------------------------------------------------------------| -| documents | _JSONObject_ | An array of documents (_JSONObject_ s) to be inserted into the datastore | +| 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 | #### Example @@ -1286,7 +1118,7 @@ This method inserts documents into Grandeur datastore. Project myProject; Datastore myDatastore; -void insertCallback(JSONObject insertionResult) { +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."); @@ -1301,7 +1133,7 @@ void setup() { void loop() { // This inserts a document containing voltage and current readings in datastore on every loop. - JSONObject docs; + Var docs; // Adding voltage and current readings to the first document of docs array. // In JSON, the docs array looks like this: // [{"voltage": analogRead(A0), "current": analogRead(A1)}] @@ -1321,7 +1153,7 @@ void loop() { ### search -> search (filter: _JSONObject_, projection: _JSONObject_, pageNumber: _int_, callback: _Callback_) : returns _void_ +> 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`. @@ -1329,8 +1161,8 @@ This method searches for documents in Grandeur datastore based on the `filter` s | Name | Type | Description | |-------------|--------------|--------------------------------------------------------------------------------------------| -| filter | _JSONObject_ | A document that describes the conditions which the documents should satisfy to be returned | -| projection | _JSONObject_ | A document that describes what fields to return | +| 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 | @@ -1340,7 +1172,7 @@ This method searches for documents in Grandeur datastore based on the `filter` s Project myProject; Datastore myDatastore; -void searchCallback(JSONObject searchResult) { +void searchCallback(Var searchResult) { // This method just prints the documents if the search is successful. if(searchResult["code"] == "DATASTORE-DOCUMENTS-FETCHED") { Serial.print("Documents fetched from Grandeur: "); @@ -1415,7 +1247,7 @@ Here are some enhancements that we are considering to implement in the SDK. They [summary]: #device-registry "Summary" [parms]: #device-registry "Parms" [callback]: #the-dexterity-of-arduino-sdk "The Dexterity of Arduino SDK" -[jsonobject]: #the-dexterity-of-arduino-sdk "The Dexterity of Arduino SDK" +[var]: #the-dexterity-of-arduino-sdk "The Dexterity of Arduino SDK" [the dexterity of arduino sdk]: #the-dexterity-of-arduino-sdk "The Dexterity of Arduino SDK" [models]: #models "Models" [apikey]: #project "Project" diff --git a/examples/CrossListening/CrossListening-esp32/CrossListening-esp32.ino b/examples/CrossListening/CrossListening-esp32/CrossListening-esp32.ino index 6d3b4be..c4dede3 100644 --- a/examples/CrossListening/CrossListening-esp32/CrossListening-esp32.ino +++ b/examples/CrossListening/CrossListening-esp32/CrossListening-esp32.ino @@ -29,60 +29,50 @@ String token = "YOUR-ACCESS-TOKEN"; const char* ssid = "YOUR-WIFI-SSID"; const char* passphrase = "YOUR-WIFI-PASSWORD"; -// Declaring and initializing other variables +// Declaring and initializing other Variables unsigned long current = millis(); -Project myProject; -Device myDevice; +Project project; +Data data; int statePin = 4; int voltagePin = 2; // Function prototypes void WiFiEventCallback(WiFiEvent_t event); void setupWiFi(void); -void connectionCallback(JSONObject updateObject); -void initializeState(JSONObject getResult); -void parmsUpdatedCallback(JSONObject updatedParms); -void summarySetCallback(JSONObject setResult); -void parmsSetCallback(JSONObject setResult); - +void connectionCallback(bool state); +void initializeState(Var getResult); +void stateUpdatedCallback(bool state, const char* path); +void voltageSetCallback(Var setResult); void setup() { Serial.begin(9600); // This sets up the device WiFi. setupWiFi(); - // This initializes the SDK's configurations and returns a new object of Project class. - myProject = grandeur.init(apiKey, token); - // Getting object of Device class. - myDevice = myProject.device(deviceID); - // This schedules the connectionCallback() function to be called when connection with the cloud + // This initializes the SDK's configurations and returns reference to your project. + project = grandeur.init(apiKey, token); + // Getting reference to your device. + data = project.device(deviceID).data(); + // This schedules the connectionCallback() function to be called when connection with Grandeur // is made/broken. - myProject.onConnection(connectionCallback); - // This schedules parmsUpdatedCallback() function to be called when variable stored - // in device's parms are changed on Grandeur. - myDevice.onParms(parmsUpdatedCallback); + project.onConnection(connectionCallback); + // This schedules stateUpdatedCallback() function to be called when the device state is + // changed on Grandeur. + data.on("state", stateUpdatedCallback); } void loop() { // In this loop() function, after every five seconds, we send the updated values of our // device's voltage and state to Grandeur. - if(myProject.isConnected()) { + if(project.isConnected()) { if(millis() - current >= 5000) { // This if-condition makes sure that the code inside this block runs only after // every five seconds. - Serial.println("Setting Summary"); - JSONObject summary; - summary["voltage"] = analogRead(voltagePin); - // This updates the summary of our device on Grandeur and schedules summarySetCallback() - // function to be called when Grandeur responds with the SUMMARY UPDATED message. - myDevice.setSummary(summary, summarySetCallback); - - Serial.println("Setting Parms"); - JSONObject parms; - parms["state"] = digitalRead(statePin); - // This updates the parms of our device on Grandeur and schedules parmsSetCallback() - // function to be called when Grandeur responds with the PARMS UPDATED message. - myDevice.setParms(parms, parmsSetCallback); + Serial.println("Setting Voltage"); + int voltage = analogRead(voltagePin); + // This updates the voltage of our device on Grandeur and schedules voltageSetCallback() + // function to be called when Grandeur responds with the DATA UPDATED message. + data.set("voltage", voltage, voltageSetCallback); // This updates the millis counter for // the five seconds scheduler. @@ -91,7 +81,7 @@ void loop() { } // This runs the SDK only when the WiFi is connected. - myProject.loop(WiFi.status() == WL_CONNECTED); + project.loop(WiFi.status() == WL_CONNECTED); } void WiFiEventCallback(WiFiEvent_t event) { @@ -121,69 +111,56 @@ void setupWiFi(void) { Serial.printf("\nDevice is connecting to WiFi using SSID %s and Passphrase %s.\n", ssid, passphrase); } -void connectionCallback(bool state) { - switch(state) { +void connectionCallback(bool status) { + switch(status) { case CONNECTED: - // On successful connection with the cloud, we initialize the device's *state*. - // To do that, we get device parms from the cloud and set the *state pin* to the - // value of *state* in those parms. - Serial.println("Device is connected to the cloud."); - myDevice.getParms(initializeState); - Serial.println("Listening for parms update from the cloud..."); + // On successful connection with Grandeur, we initialize the device's *state*. + // To do that, we get device state from Grandeur and set the *state pin* to its + // value. + Serial.println("Device is connected with Grandeur."); + data.get("state", initializeState); + Serial.println("Listening for state update from Grandeur..."); // Initializing the millis counter for the five // seconds timer. current = millis(); break; case DISCONNECTED: - Serial.println("Device is disconnected from the cloud."); + Serial.println("Device's connection with Grandeur is broken."); break; } } -void initializeState(JSONObject getResult) { - // This function sets the *state pin* to the *state value* that we received in parms - // from the cloud. - if(getResult["code"] == "DEVICE-PARMS-FETCHED") { - int state = getResult["deviceParms"]["state"]; +void initializeState(Var getResult) { + // This function sets the *state pin* to the *state value* that we received in data + // from Grandeur. + if(getResult["code"] == "DEVICE-DATA-FETCHED") { + int state = getResult["data"]; + Serial.printf("State is: %d\n", state); digitalWrite(statePin, state); return; } - // If the parms could not be fetched. - Serial.println("Failed to Fetch Parms"); + // If the data could not be fetched. + Serial.println("Failed to Fetch State"); return; } -void parmsUpdatedCallback(JSONObject updatedParms) { - // This function gets the *updated state* from the device parms and set the *state pin* - // with *state value*. - Serial.printf("Updated State is: %d\n", (bool) updatedParms["state"]); - digitalWrite(statePin, (bool) updatedParms["state"]); +void stateUpdatedCallback(bool state, const char* path) { + // This function gets the *updated state* from Grandeur and set the *state pin* + // with its value. + Serial.printf("Updated State is: %d\n", state); + digitalWrite(statePin, state); } -void summarySetCallback(JSONObject setResult) { - if(setResult["code"] == "DEVICE-SUMMARY-UPDATED") { - Serial.printf("Voltage is updated to: %d\n", (int) setResult["update"]["voltage"]); +void voltageSetCallback(Var setResult) { + if(setResult["code"] == "DEVICE-DATA-UPDATED") { + Serial.printf("Voltage is updated to: %d\n", (int) setResult["update"]); /* You can set some pins or trigger events here which depend on successful - ** device summary update. + ** voltage update. */ return; } - // If the summary could not be updated. - Serial.println("Failed to Update Summary"); + // If the voltage could not be updated. + Serial.println("Failed to Update Voltage"); return; -} - -void parmsSetCallback(JSONObject setResult) { - if(setResult["code"] == "DEVICE-PARMS-UPDATED") { - Serial.printf("State is updated to: %d\n", (bool) setResult["update"]["state"]); - - /* You can set some pins or trigger events here which depend on successful - ** device parms update. - */ - return; - } - // If the parms could not be updated. - Serial.println("Failed to Update Parms"); - return; -} +} \ No newline at end of file diff --git a/examples/CrossListening/CrossListening-esp8266/CrossListening-esp8266.ino b/examples/CrossListening/CrossListening-esp8266/CrossListening-esp8266.ino index a02c65f..e811865 100644 --- a/examples/CrossListening/CrossListening-esp8266/CrossListening-esp8266.ino +++ b/examples/CrossListening/CrossListening-esp8266/CrossListening-esp8266.ino @@ -40,50 +40,40 @@ int voltagePin = A0; // Function prototypes void setupWiFi(void); -void connectionCallback(JSONObject updateObject); -void initializeState(JSONObject getResult); -void parmsUpdatedCallback(JSONObject updatedParms); -void summarySetCallback(JSONObject setResult); -void parmsSetCallback(JSONObject setResult); - +void connectionCallback(bool state); +void initializeState(Var getResult); +void stateUpdatedCallback(bool state, const char* path); +void voltageSetCallback(Var setResult); void setup() { Serial.begin(9600); // This sets up the device WiFi. setupWiFi(); - // This initializes the SDK's configurations and returns a new object of Project class. - myProject = grandeur.init(apiKey, token); - // Getting object of Device class. - myDevice = myProject.device(deviceID); - // This schedules the connectionCallback() function to be called when connection with the cloud + // This initializes the SDK's configurations and returns reference to your project. + project = grandeur.init(apiKey, token); + // Getting reference to your device. + data = project.device(deviceID).data(); + // This schedules the connectionCallback() function to be called when connection with Grandeur // is made/broken. - myProject.onConnection(connectionCallback); - // This schedules parmsUpdatedCallback() function to be called when variable stored - // in device's parms are changed on Grandeur. - myDevice.onParms(parmsUpdatedCallback); + project.onConnection(connectionCallback); + // This schedules stateUpdatedCallback() function to be called when the device state is + // changed on Grandeur. + data.on("state", stateUpdatedCallback); } void loop() { // In this loop() function, after every five seconds, we send the updated values of our // device's voltage and state to Grandeur. - if(myProject.isConnected()) { + if(project.isConnected()) { if(millis() - current >= 5000) { // This if-condition makes sure that the code inside this block runs only after // every five seconds. - Serial.println("Setting Summary"); - JSONObject summary; - summary["voltage"] = analogRead(voltagePin); - // This updates the summary of our device on Grandeur and schedules summarySetCallback() - // function to be called when Grandeur responds with the SUMMARY UPDATED message. - myDevice.setSummary(summary, summarySetCallback); - - Serial.println("Setting Parms"); - JSONObject parms; - parms["state"] = digitalRead(statePin); - // This updates the parms of our device on Grandeur and schedules parmsSetCallback() - // function to be called when Grandeur responds with the PARMS UPDATED message. - myDevice.setParms(parms, parmsSetCallback); + Serial.println("Setting Voltage"); + int voltage = analogRead(voltagePin); + // This updates the voltage of our device on Grandeur and schedules voltageSetCallback() + // function to be called when Grandeur responds with the DATA UPDATED message. + data.set("voltage", voltage, voltageSetCallback); // This updates the millis counter for // the five seconds scheduler. @@ -92,7 +82,7 @@ void loop() { } // This runs the SDK only when the WiFi is connected. - myProject.loop(WiFi.status() == WL_CONNECTED); + project.loop(WiFi.status() == WL_CONNECTED); } void setupWiFi(void) { @@ -115,69 +105,56 @@ void setupWiFi(void) { Serial.printf("\nDevice is connecting to WiFi using SSID %s and Passphrase %s.\n", ssid.c_str(), passphrase.c_str()); } -void connectionCallback(bool state) { - switch(state) { +void connectionCallback(bool status) { + switch(status) { case CONNECTED: - // On successful connection with the cloud, we initialize the device's *state*. - // To do that, we get device parms from the cloud and set the *state pin* to the - // value of *state* in those parms. - Serial.println("Device is connected to the cloud."); - myDevice.getParms(initializeState); - Serial.println("Listening for parms update from the cloud..."); + // On successful connection with Grandeur, we initialize the device's *state*. + // To do that, we get device state from Grandeur and set the *state pin* to its + // value. + Serial.println("Device is connected with Grandeur."); + data.get("state", initializeState); + Serial.println("Listening for state update from Grandeur..."); // Initializing the millis counter for the five // seconds timer. current = millis(); break; case DISCONNECTED: - Serial.println("Device is disconnected from the cloud."); + Serial.println("Device's connection with Grandeur is broken."); break; } } -void initializeState(JSONObject getResult) { - // This function sets the *state pin* to the *state value* that we received in parms - // from the cloud. - if(getResult["code"] == "DEVICE-PARMS-FETCHED") { - int state = getResult["deviceParms"]["state"]; +void initializeState(Var getResult) { + // This function sets the *state pin* to the *state value* that we received in data + // from Grandeur. + if(getResult["code"] == "DEVICE-DATA-FETCHED") { + int state = getResult["data"]; + Serial.printf("State is: %d\n", state); digitalWrite(statePin, state); return; } - // If the parms could not be fetched. - Serial.println("Failed to Fetch Parms"); + // If the data could not be fetched. + Serial.println("Failed to Fetch State"); return; } -void parmsUpdatedCallback(JSONObject updatedParms) { - // This function gets the *updated state* from the device parms and set the *state pin* - // with *state value*. - Serial.printf("Updated State is: %d\n", (bool) updatedParms["state"]); - digitalWrite(statePin, (bool) updatedParms["state"]); +void stateUpdatedCallback(bool state, const char* path) { + // This function gets the *updated state* from Grandeur and set the *state pin* + // with its value. + Serial.printf("Updated State is: %d\n", state); + digitalWrite(statePin, state); } -void summarySetCallback(JSONObject setResult) { - if(setResult["code"] == "DEVICE-SUMMARY-UPDATED") { - Serial.printf("Voltage is updated to: %d\n", (int) setResult["update"]["voltage"]); +void voltageSetCallback(Var setResult) { + if(setResult["code"] == "DEVICE-DATA-UPDATED") { + Serial.printf("Voltage is updated to: %d\n", (int) setResult["update"]); /* You can set some pins or trigger events here which depend on successful - ** device summary update. - */ - return; - } - // If the summary could not be updated. - Serial.println("Failed to Update Summary"); - return; -} - -void parmsSetCallback(JSONObject setResult) { - if(setResult["code"] == "DEVICE-PARMS-UPDATED") { - Serial.printf("State is updated to: %d\n", (bool) setResult["update"]["state"]); - - /* You can set some pins or trigger events here which depend on successful - ** device parms update. + ** voltage update. */ return; } - // If the parms could not be updated. - Serial.println("Failed to Update Parms"); + // If the voltage could not be updated. + Serial.println("Failed to Update Voltage"); return; } \ No newline at end of file diff --git a/examples/DashListening-App/DashListening-App-esp32/DashListening-App-esp32.ino b/examples/DashListening-App/DashListening-App-esp32/DashListening-App-esp32.ino index 4a71534..bc5dc87 100644 --- a/examples/DashListening-App/DashListening-App-esp32/DashListening-App-esp32.ino +++ b/examples/DashListening-App/DashListening-App-esp32/DashListening-App-esp32.ino @@ -36,9 +36,8 @@ int voltagePin = 2; void WiFiEventCallback(WiFiEvent_t event); void setupWiFi(void); void connectionCallback(bool state); -void initializeState(JSONObject getResult); -void summarySetCallback(JSONObject setResult); -void parmsSetCallback(JSONObject setResult); +void initializeState(Var getResult); +void voltageSetCallback(Var setResult); void setup() { Serial.begin(9600); @@ -48,7 +47,7 @@ void setup() { myProject = grandeur.init(apiKey, token); // Getting object of Device class. myDevice = myProject.device(deviceID); - // This schedules the connectionCallback() function to be called when connection with the cloud + // This schedules the connectionCallback() function to be called when connection with Grandeur // is made/broken. myProject.onConnection(connectionCallback); } @@ -61,19 +60,11 @@ void loop() { // This if-condition makes sure that the code inside this block runs only after // every five seconds. - Serial.println("Setting Summary"); - JSONObject summary; - summary["voltage"] = analogRead(voltagePin); - // This updates the summary of our device on Grandeur and schedules summarySetCallback() - // function to be called when Grandeur responds with the SUMMARY UPDATED message. - myDevice.setSummary(summary, summarySetCallback); - - Serial.println("Setting Parms"); - JSONObject parms; - parms["state"] = digitalRead(statePin); - // This updates the parms of our device on Grandeur and schedules parmsSetCallback() - // function to be called when Grandeur responds with the PARMS UPDATED message. - myDevice.setParms(parms, parmsSetCallback); + Serial.println("Setting Voltage"); + int voltage = analogRead(voltagePin); + // This updates the voltage variable and schedules voltageSetCallback() + // function to be called when Grandeur acknowledges the update. + myDevice.data().set("voltage", voltage, voltageSetCallback); // This updates the millis counter for // the five seconds scheduler. @@ -115,58 +106,43 @@ void setupWiFi(void) { void connectionCallback(bool state) { switch(state) { case CONNECTED: - // On successful connection with the cloud, we initialize the device's *state*. - // To do that, we get device parms from the cloud and set the *state pin* to the value of *state* in those parms. - Serial.println("Device is connected to the cloud."); - myDevice.getParms(initializeState); + // On successful connection with Grandeur, we initialize the device's *state*. + // To do that, we set the *state pin* to the value of *state* from Grandeur. + Serial.println("Device is connected with Grandeur."); + myDevice.data().get("state", initializeState); // Initializing the millis counter for the five // seconds timer. current = millis(); break; case DISCONNECTED: - Serial.println("Device is disconnected from the cloud."); + Serial.println("Device's connection with Grandeur is broken."); break; } } -void initializeState(JSONObject getResult) { - // This function sets the *state pin* to the *state value* that we received in parms - // from the cloud. - if(getResult["code"] == "DEVICE-PARMS-FETCHED") { - int state = getResult["deviceParms"]["state"]; +void initializeState(Var getResult) { + // This function sets the *state pin* to the *state value*. + if(getResult["code"] == "DEVICE-DATA-FETCHED") { + int state = getResult["data"]; digitalWrite(statePin, state); return; } - // If the parms could not be fetched. - Serial.println("Failed to Fetch Parms"); + // If the state could not be fetched. + Serial.println("Failed to Fetch State"); return; } -void summarySetCallback(JSONObject setResult) { - if(setResult["code"] == "DEVICE-SUMMARY-UPDATED") { - Serial.printf("Voltage is updated to: %d\n", (int) setResult["update"]["voltage"]); +void voltageSetCallback(Var setResult) { + if(setResult["code"] == "DEVICE-DATA-UPDATED") { + Serial.printf("Voltage is updated to: %d\n", (int) setResult["update"]); /* You can set some pins or trigger events here which depend on successful - ** device summary update. - */ - return; - } - // If the summary could not be updated. - Serial.println("Failed to Update Summary"); - return; -} - -void parmsSetCallback(JSONObject setResult) { - if(setResult["code"] == "DEVICE-PARMS-UPDATED") { - Serial.printf("State is updated to: %d\n", (bool) setResult["update"]["state"]); - - /* You can set some pins or trigger events here which depend on successful - ** device parms update. + ** voltage update. */ return; } - // If the parms could not be updated. - Serial.println("Failed to Update Parms"); + // If the voltage could not be updated. + Serial.println("Failed to Update Voltage"); return; -} +} \ No newline at end of file diff --git a/examples/DashListening-App/DashListening-App-esp8266/DashListening-App-esp8266.ino b/examples/DashListening-App/DashListening-App-esp8266/DashListening-App-esp8266.ino index 0998efb..7d04de4 100644 --- a/examples/DashListening-App/DashListening-App-esp8266/DashListening-App-esp8266.ino +++ b/examples/DashListening-App/DashListening-App-esp8266/DashListening-App-esp8266.ino @@ -37,9 +37,8 @@ int voltagePin = A0; // Function prototypes void setupWiFi(void); void connectionCallback(bool state); -void initializeState(JSONObject getResult); -void summarySetCallback(JSONObject setResult); -void parmsSetCallback(JSONObject setResult); +void initializeState(Var getResult); +void voltageSetCallback(Var setResult); void setup() { Serial.begin(9600); @@ -49,7 +48,7 @@ void setup() { myProject = grandeur.init(apiKey, token); // Getting object of Device class. myDevice = myProject.device(deviceID); - // This schedules the connectionCallback() function to be called when connection with the cloud + // This schedules the connectionCallback() function to be called when connection with Grandeur // is made/broken. myProject.onConnection(connectionCallback); } @@ -62,19 +61,11 @@ void loop() { // This if-condition makes sure that the code inside this block runs only after // every five seconds. - Serial.println("Setting Summary"); - JSONObject summary; - summary["voltage"] = analogRead(voltagePin); - // This updates the summary of our device on Grandeur and schedules summarySetCallback() - // function to be called when Grandeur responds with the SUMMARY UPDATED message. - myDevice.setSummary(summary, summarySetCallback); - - Serial.println("Setting Parms"); - JSONObject parms; - parms["state"] = digitalRead(statePin); - // This updates the parms of our device on Grandeur and schedules parmsSetCallback() - // function to be called when Grandeur responds with the PARMS UPDATED message. - myDevice.setParms(parms, parmsSetCallback); + Serial.println("Setting Voltage"); + int voltage = analogRead(voltagePin); + // This updates the voltage variable and schedules voltageSetCallback() + // function to be called when Grandeur acknowledges the update. + myDevice.data().set("voltage", voltage, voltageSetCallback); // This updates the millis counter for // the five seconds scheduler. @@ -109,58 +100,43 @@ void setupWiFi(void) { void connectionCallback(bool state) { switch(state) { case CONNECTED: - // On successful connection with the cloud, we initialize the device's *state*. - // To do that, we get device parms from the cloud and set the *state pin* to the value of *state* in those parms. - Serial.println("Device is connected to the cloud."); - myDevice.getParms(initializeState); + // On successful connection with Grandeur, we initialize the device's *state*. + // To do that, we set the *state pin* to the value of *state* from Grandeur. + Serial.println("Device is connected with Grandeur."); + myDevice.data().get("state", initializeState); // Initializing the millis counter for the five // seconds timer. current = millis(); break; case DISCONNECTED: - Serial.println("Device is disconnected from the cloud."); + Serial.println("Device's connection with Grandeur is broken."); break; } } -void initializeState(JSONObject getResult) { - // This function sets the *state pin* to the *state value* that we received in parms - // from the cloud. - if(getResult["code"] == "DEVICE-PARMS-FETCHED") { - int state = getResult["deviceParms"]["state"]; +void initializeState(Var getResult) { + // This function sets the *state pin* to the *state value*. + if(getResult["code"] == "DEVICE-DATA-FETCHED") { + int state = getResult["data"]; digitalWrite(statePin, state); return; } - // If the parms could not be fetched. - Serial.println("Failed to Fetch Parms"); + // If the state could not be fetched. + Serial.println("Failed to Fetch State"); return; } -void summarySetCallback(JSONObject setResult) { - if(setResult["code"] == "DEVICE-SUMMARY-UPDATED") { - Serial.printf("Voltage is updated to: %d\n", (int) setResult["update"]["voltage"]); +void voltageSetCallback(Var setResult) { + if(setResult["code"] == "DEVICE-DATA-UPDATED") { + Serial.printf("Voltage is updated to: %d\n", (int) setResult["update"]); /* You can set some pins or trigger events here which depend on successful - ** device summary update. - */ - return; - } - // If the summary could not be updated. - Serial.println("Failed to Update Summary"); - return; -} - -void parmsSetCallback(JSONObject setResult) { - if(setResult["code"] == "DEVICE-PARMS-UPDATED") { - Serial.printf("State is updated to: %d\n", (bool) setResult["update"]["state"]); - - /* You can set some pins or trigger events here which depend on successful - ** device parms update. + ** voltage update. */ return; } - // If the parms could not be updated. - Serial.println("Failed to Update Parms"); + // If the voltage could not be updated. + Serial.println("Failed to Update Voltage"); return; } \ No newline at end of file diff --git a/examples/DashListening-Device/DashListening-Device-esp32/DashListening-Device-esp32.ino b/examples/DashListening-Device/DashListening-Device-esp32/DashListening-Device-esp32.ino index bc12bde..5fb8154 100644 --- a/examples/DashListening-Device/DashListening-Device-esp32/DashListening-Device-esp32.ino +++ b/examples/DashListening-Device/DashListening-Device-esp32/DashListening-Device-esp32.ino @@ -34,23 +34,23 @@ int statePin = 4; void WiFiEventCallback(WiFiEvent_t event); void setupWiFi(void); void connectionCallback(bool state); -void initializeState(JSONObject getResult); -void parmsUpdatedCallback(JSONObject updatedParms); +void initializeState(Var getResult); +void stateUpdatedCallback(bool state, const char* path); void setup() { Serial.begin(9600); // This sets up the device WiFi. setupWiFi(); - // This initializes the SDK's configurations and returns a new object of GrandeurDevice class. + // This initializes the SDK's configurations and returns a reference to object of the Project class. myProject = grandeur.init(apiKey, token); - // Getting object of Device class. + // Getting reference to object of Device class. myDevice = myProject.device(deviceID); - // This schedules the connectionCallback() function to be called when connection with the cloud + // This schedules the connectionCallback() function to be called when connection with Grandeur // is made/broken. myProject.onConnection(connectionCallback); - // This schedules parmsUpdatedCallback() function to be called when variable stored - // in device's parms are changed on Grandeur. - myDevice.onParms(parmsUpdatedCallback); + // This schedules stateUpdatedCallback() function to be called when state variable + // is changed on Grandeur. + myDevice.data().on("state", stateUpdatedCallback); } void loop() { @@ -88,35 +88,32 @@ void setupWiFi(void) { void connectionCallback(bool status) { switch(status) { case CONNECTED: - // On successful connection with the cloud, we initialize the device's *state*. - // To do that, we get device parms from the cloud and set the *state pin* to the - // value of *state* in those parms. - Serial.println("Device is connected to the cloud."); - myDevice.getParms(initializeState); - Serial.println("Listening for parms update from the cloud..."); + // On successful connection with the cloud, we initialize the device's *state* pin. + // To do that, we get state variable from Grandeur and set the *state pin* to its value. + Serial.println("Device is connected with Grandeur."); + myDevice.data().get("state", initializeState); + Serial.println("Listening for update in state..."); break; case DISCONNECTED: - Serial.println("Device is disconnected from the cloud."); + Serial.println("Device's connection with Grandeur is broken."); break; } } -void initializeState(JSONObject getResult) { - // This function sets the *state pin* to the *state value* that we received in parms - // from the cloud. - if(getResult["code"] == "DEVICE-PARMS-FETCHED") { - int state = getResult["deviceParms"]["state"]; +void initializeState(Var getResult) { + // This function sets the *state pin* to the *state value* that we received from Grandeur. + if(getResult["code"] == "DEVICE-DATA-FETCHED") { + int state = getResult["data"]; digitalWrite(statePin, state); return; } - // If the parms could not be fetched. - Serial.println("Failed to Fetch Parms"); + // If the state could not be fetched. + Serial.println("Failed to Fetch State"); return; } -void parmsUpdatedCallback(JSONObject updatedParms) { - // This function gets the *updated state* from the device parms and set the *state pin* - // with *state value*. - Serial.printf("Updated State is: %d\n", (bool) updatedParms["state"]); - digitalWrite(statePin, (bool) updatedParms["state"]); +void stateUpdatedCallback(bool state, const char* path) { + // This function sets the *state pin* to *state value*. + Serial.printf("Updated State is: %d\n", state); + digitalWrite(statePin, state); } \ No newline at end of file diff --git a/examples/DashListening-Device/DashListening-Device-esp8266/DashListening-Device-esp8266.ino b/examples/DashListening-Device/DashListening-Device-esp8266/DashListening-Device-esp8266.ino index d95a63c..40df911 100644 --- a/examples/DashListening-Device/DashListening-Device-esp8266/DashListening-Device-esp8266.ino +++ b/examples/DashListening-Device/DashListening-Device-esp8266/DashListening-Device-esp8266.ino @@ -35,23 +35,23 @@ int statePin = D0; // Function prototypes void setupWiFi(void); void connectionCallback(bool state); -void initializeState(JSONObject getResult); -void parmsUpdatedCallback(JSONObject updatedParms); +void initializeState(Var getResult); +void stateUpdatedCallback(bool state, const char* path); void setup() { Serial.begin(9600); // This sets up the device WiFi. setupWiFi(); - // This initializes the SDK's configurations and returns a new object of GrandeurDevice class. + // This initializes the SDK's configurations and returns a reference to object of the Project class. myProject = grandeur.init(apiKey, token); - // Getting object of Device class. + // Getting reference to object of Device class. myDevice = myProject.device(deviceID); - // This schedules the connectionCallback() function to be called when connection with the cloud + // This schedules the connectionCallback() function to be called when connection with Grandeur // is made/broken. myProject.onConnection(connectionCallback); - // This schedules parmsUpdatedCallback() function to be called when variable stored - // in device's parms are changed on Grandeur. - myDevice.onParms(parmsUpdatedCallback); + // This schedules stateUpdatedCallback() function to be called when state variable + // is changed on Grandeur. + myDevice.data().on("state", stateUpdatedCallback); } void loop() { @@ -82,35 +82,32 @@ void setupWiFi(void) { void connectionCallback(bool status) { switch(status) { case CONNECTED: - // On successful connection with the cloud, we initialize the device's *state*. - // To do that, we get device parms from the cloud and set the *state pin* to the - // value of *state* in those parms. - Serial.println("Device is connected to the cloud."); - myDevice.getParms(initializeState); - Serial.println("Listening for parms update from the cloud..."); + // On successful connection with the cloud, we initialize the device's *state* pin. + // To do that, we get state variable from Grandeur and set the *state pin* to its value. + Serial.println("Device is connected with Grandeur."); + myDevice.data().get("state", initializeState); + Serial.println("Listening for update in state..."); break; case DISCONNECTED: - Serial.println("Device is disconnected from the cloud."); + Serial.println("Device's connection with Grandeur is broken."); break; } } -void initializeState(JSONObject getResult) { - // This function sets the *state pin* to the *state value* that we received in parms - // from the cloud. - if(getResult["code"] == "DEVICE-PARMS-FETCHED") { - int state = getResult["deviceParms"]["state"]; +void initializeState(Var getResult) { + // This function sets the *state pin* to the *state value* that we received from Grandeur. + if(getResult["code"] == "DEVICE-DATA-FETCHED") { + int state = getResult["data"]; digitalWrite(statePin, state); return; } - // If the parms could not be fetched. - Serial.println("Failed to Fetch Parms"); + // If the state could not be fetched. + Serial.println("Failed to Fetch State"); return; } -void parmsUpdatedCallback(JSONObject updatedParms) { - // This function gets the *updated state* from the device parms and set the *state pin* - // with *state value*. - Serial.printf("Updated State is: %d\n", (bool) updatedParms["state"]); - digitalWrite(statePin, (bool) updatedParms["state"]); +void stateUpdatedCallback(bool state, const char* path) { + // This function sets the *state pin* to *state value*. + Serial.printf("Updated State is: %d\n", state); + digitalWrite(statePin, state); } \ No newline at end of file diff --git a/examples/Datastore/Logging/Logging-esp32/Logging-esp32.ino b/examples/Datastore/Logging/Logging-esp32/Logging-esp32.ino index d40552a..c35b11e 100644 --- a/examples/Datastore/Logging/Logging-esp32/Logging-esp32.ino +++ b/examples/Datastore/Logging/Logging-esp32/Logging-esp32.ino @@ -32,7 +32,7 @@ unsigned long current = millis(); void WiFiEventCallback(WiFiEvent_t event); void setupWiFi(void); void connectionCallback(bool status); -void insertCallback(JSONObject payload); +void insertCallback(Var payload); void setup() { Serial.begin(9600); @@ -42,7 +42,7 @@ void setup() { myProject = grandeur.init(apiKey, token); // Getting object of Datastore class. myDatastore = myProject.datastore(); - // This schedules the connectionCallback() function to be called when connection with the cloud + // This schedules the connectionCallback() function to be called when connection with Grandeur // is made/broken. myProject.onConnection(connectionCallback); } @@ -52,7 +52,7 @@ void loop() { if(millis() - current >= 5000) { // This if-condition makes sure that the code inside this block runs only after // every five seconds. - JSONObject logs; + Var logs; logs[0]["voltage"] = analogRead(A0); myDatastore.collection("logs").insert(logs, insertCallback); // This updates the millis counter for @@ -95,19 +95,18 @@ void setupWiFi(void) { void connectionCallback(bool status) { switch(status) { case CONNECTED: - // On successful connection with the cloud, we initialize the device's *state*. - // To do that, we get device parms from the cloud and set the *state pin* to the - // value of *state* in those parms. - Serial.println("Device is connected to the cloud."); + // On successful connection with Grandeur, we initialize the device's *state*. + // To do that, we set the *state pin* to the value of *state* from Grandeur. + Serial.println("Device is connected with Grandeur."); Serial.println("Logging voltage to Grandeur..."); break; case DISCONNECTED: - Serial.println("Device is disconnected from the cloud."); + Serial.println("Device's connection with Grandeur is broken."); break; } } -void insertCallback(JSONObject insertResult) { +void insertCallback(Var insertResult) { // This function prints if the logs were successfully inserted into the datastore or not. if(insertResult["code"] == "DATASTORE-DOCUMENTS-INSERTED") { Serial.println("Voltage is successfully logged to Grandeur."); diff --git a/examples/Datastore/Logging/Logging-esp8266/Logging-esp8266.ino b/examples/Datastore/Logging/Logging-esp8266/Logging-esp8266.ino index 551d172..f0df605 100644 --- a/examples/Datastore/Logging/Logging-esp8266/Logging-esp8266.ino +++ b/examples/Datastore/Logging/Logging-esp8266/Logging-esp8266.ino @@ -33,7 +33,7 @@ unsigned long current = millis(); // Function prototypes void setupWiFi(void); void connectionCallback(bool status); -void insertCallback(JSONObject payload); +void insertCallback(Var payload); void setup() { Serial.begin(9600); @@ -43,7 +43,7 @@ void setup() { myProject = grandeur.init(apiKey, token); // Getting object of Datastore class. myDatastore = myProject.datastore(); - // This schedules the connectionCallback() function to be called when connection with the cloud + // This schedules the connectionCallback() function to be called when connection with Grandeur // is made/broken. myProject.onConnection(connectionCallback); } @@ -53,7 +53,7 @@ void loop() { if(millis() - current >= 5000) { // This if-condition makes sure that the code inside this block runs only after // every five seconds. - JSONObject logs; + Var logs; logs[0]["voltage"] = analogRead(A0); myDatastore.collection("logs").insert(logs, insertCallback); // This updates the millis counter for @@ -89,19 +89,18 @@ void setupWiFi(void) { void connectionCallback(bool status) { switch(status) { case CONNECTED: - // On successful connection with the cloud, we initialize the device's *state*. - // To do that, we get device parms from the cloud and set the *state pin* to the - // value of *state* in those parms. - Serial.println("Device is connected to the cloud."); + // On successful connection with Grandeur, we initialize the device's *state*. + // To do that, we set the *state pin* to the value of *state* from Grandeur. + Serial.println("Device is connected with Grandeur."); Serial.println("Logging voltage to Grandeur..."); break; case DISCONNECTED: - Serial.println("Device is disconnected from the cloud."); + Serial.println("Device's connection with Grandeur is broken."); break; } } -void insertCallback(JSONObject insertResult) { +void insertCallback(Var insertResult) { // This function prints if the logs were successfully inserted into the datastore or not. if(insertResult["code"] == "DATASTORE-DOCUMENTS-INSERTED") { Serial.println("Voltage is successfully logged to Grandeur."); diff --git a/examples/Datastore/Searching/Searching-esp32/Searching-esp32.ino b/examples/Datastore/Searching/Searching-esp32/Searching-esp32.ino index c189b85..b4af928 100644 --- a/examples/Datastore/Searching/Searching-esp32/Searching-esp32.ino +++ b/examples/Datastore/Searching/Searching-esp32/Searching-esp32.ino @@ -32,7 +32,7 @@ unsigned long current = millis(); void WiFiEventCallback(WiFiEvent_t event); void setupWiFi(void); void connectionCallback(bool status); -void searchCallback(JSONObject payload); +void searchCallback(Var payload); void setup() { Serial.begin(9600); @@ -42,7 +42,7 @@ void setup() { myProject = grandeur.init(apiKey, token); // Getting object of Datastore class. myDatastore = myProject.datastore(); - // This schedules the connectionCallback() function to be called when connection with the cloud + // This schedules the connectionCallback() function to be called when connection with Grandeur // is made/broken. myProject.onConnection(connectionCallback); } @@ -53,7 +53,7 @@ void loop() { // This if-condition makes sure that the code inside this block runs only after // every five seconds. // This fetches 1st page of all the documents stored in the datastore. - JSONObject filter; + Var filter; filter["voltage"]["$gt"] = 1; myDatastore.collection("logs").search(filter, {}, 0, searchCallback); // This updates the millis counter for @@ -96,19 +96,18 @@ void setupWiFi(void) { void connectionCallback(bool status) { switch(status) { case CONNECTED: - // On successful connection with the cloud, we initialize the device's *state*. - // To do that, we get device parms from the cloud and set the *state pin* to the - // value of *state* in those parms. - Serial.println("Device is connected to the cloud."); - Serial.println("Fetching documents from Grandeur..."); + // On successful connection with Grandeur, we initialize the device's *state*. + // To do that, we set the *state pin* to the value of *state* from Grandeur. + Serial.println("Device is connected with Grandeur."); + Serial.println("Logging voltage to Grandeur..."); break; case DISCONNECTED: - Serial.println("Device is disconnected from the cloud."); + Serial.println("Device's connection with Grandeur is broken."); break; } } -void searchCallback(JSONObject searchResult) { +void searchCallback(Var searchResult) { // This function prints if the datastore search for the docs was successfully or not. if(searchResult["code"] == "DATASTORE-DOCUMENTS-FETCHED") { Serial.print("Documents fetched from Grandeur: "); diff --git a/examples/Datastore/Searching/Searching-esp8266/Searching-esp8266.ino b/examples/Datastore/Searching/Searching-esp8266/Searching-esp8266.ino index 04f6a1e..c476655 100644 --- a/examples/Datastore/Searching/Searching-esp8266/Searching-esp8266.ino +++ b/examples/Datastore/Searching/Searching-esp8266/Searching-esp8266.ino @@ -33,7 +33,7 @@ unsigned long current = millis(); // Function prototypes void setupWiFi(void); void connectionCallback(bool status); -void searchCallback(JSONObject payload); +void searchCallback(Var payload); void setup() { Serial.begin(9600); @@ -43,7 +43,7 @@ void setup() { myProject = grandeur.init(apiKey, token); // Getting object of Datastore class. myDatastore = myProject.datastore(); - // This schedules the connectionCallback() function to be called when connection with the cloud + // This schedules the connectionCallback() function to be called when connection with Grandeur // is made/broken. myProject.onConnection(connectionCallback); } @@ -88,19 +88,18 @@ void setupWiFi(void) { void connectionCallback(bool status) { switch(status) { case CONNECTED: - // On successful connection with the cloud, we initialize the device's *state*. - // To do that, we get device parms from the cloud and set the *state pin* to the - // value of *state* in those parms. - Serial.println("Device is connected to the cloud."); - Serial.println("Fetching documents from Grandeur..."); + // On successful connection with Grandeur, we initialize the device's *state*. + // To do that, we set the *state pin* to the value of *state* from Grandeur. + Serial.println("Device is connected with Grandeur."); + Serial.println("Logging voltage to Grandeur..."); break; case DISCONNECTED: - Serial.println("Device is disconnected from the cloud."); + Serial.println("Device's connection with Grandeur is broken."); break; } } -void searchCallback(JSONObject searchResult) { +void searchCallback(Var searchResult) { // This function prints if the datastore search for the docs was successfully or not. if(searchResult["code"] == "DATASTORE-DOCUMENTS-FETCHED") { Serial.print("Documents fetched from Grandeur: "); diff --git a/keywords.txt b/keywords.txt index bdb20fd..9c75cfc 100644 --- a/keywords.txt +++ b/keywords.txt @@ -9,13 +9,14 @@ grandeur KEYWORD3 Project KEYWORD3 Datastore KEYWORD3 Device KEYWORD3 +Data KEYWORD3 ####################################### # Datatypes ####################################### Callback KEYWORD1 -JSONObject KEYWORD1 +Var KEYWORD1 ####################################### # Methods and Functions ####################################### @@ -28,12 +29,8 @@ stringify KEYWORD2 device KEYWORD2 datastore KEYWORD2 loop KEYWORD2 -getSummary KEYWORD2 -getParms KEYWORD2 -setSummary KEYWORD2 -setParms KEYWORD2 -onSummary KEYWORD2 -onParms KEYWORD2 +get KEYWORD2 +set KEYWORD2 collection KEYWORD2 insert KEYWORD2 remove KEYWORD2 diff --git a/src/Data.cpp b/src/Data.cpp new file mode 100644 index 0000000..e7b4bb8 --- /dev/null +++ b/src/Data.cpp @@ -0,0 +1,131 @@ +/** + * @file Device.h + * @date 23.01.2021 + * @author Grandeur Technologies + * + * Copyright (c) 2019 Grandeur Technologies LLP. All rights reserved. + * This file is part of the Arduino SDK for Grandeur. + * + */ + +#include "Data.h" + +// Constructors +Event::Event(String deviceID, DuplexHandler duplexHandler, gID id, String path) { + _duplex = duplexHandler; + _deviceID = deviceID; + _id = id; + _path = path; +} + +Event::Event() {} + +void Event::clear() { + // Clear an event handler on path + // Create a new json object + Var jsonObject; + + // Create packet + char jsonString[PACKET_SIZE]; + + // Add device id + jsonObject["deviceID"] = _deviceID; + + // Add path + jsonObject["path"] = _path; + + // Add data + jsonObject["event"] = "data"; + + // Conver the object to string + JSON.stringify(jsonObject).toCharArray(jsonString, PACKET_SIZE); + + // Send + _duplex.unsubscribe(_id, jsonString); +} + +// Constructors +Data::Data(String deviceID, DuplexHandler duplexHandler) { + _duplex = duplexHandler; + _deviceID = deviceID; +} + +Data::Data() {} + +void Data::get(const char* path, Callback callback) { + // Get data from server + // Create a new json object + Var jsonObject; + + // Create packet + char jsonString[PACKET_SIZE]; + + // Add device id + jsonObject["deviceID"] = _deviceID; + + // Add path + jsonObject["path"] = path; + + // Conver the object to string + JSON.stringify(jsonObject).toCharArray(jsonString, PACKET_SIZE); + + // Send + _duplex.send("/device/data/get", jsonString, callback); +} + +void Data::set(const char* path, Var data, Callback callback) { + // Set data to server + // Create a new json object + Var jsonObject; + + // Create packet + char jsonString[PACKET_SIZE]; + + // Add device id + jsonObject["deviceID"] = _deviceID; + + // Add path + jsonObject["path"] = path; + + // Add data + jsonObject["data"] = data; + + // Conver the object to string + JSON.stringify(jsonObject).toCharArray(jsonString, PACKET_SIZE); + + // Send + _duplex.send("/device/data/set", jsonString, callback); +} + +Event Data::on(const char* path, Callback callback) { + // Place an event handler on path update + // Create a new json object + Var jsonObject; + + // Create packet + char jsonString[PACKET_SIZE]; + + // Add device id + jsonObject["deviceID"] = _deviceID; + + // Add path + jsonObject["path"] = path; + + // Add data + jsonObject["event"] = "data"; + + // Conver the object to string + JSON.stringify(jsonObject).toCharArray(jsonString, PACKET_SIZE); + + // Convert path to string + std::string p(path); + + // Formulate the event + std::string event = "data/" + p; + + // Send + gID packetID = _duplex.subscribe(event.c_str(), jsonString, callback); + + // Return an event + return Event(_deviceID, _duplex, packetID, path); +} \ No newline at end of file diff --git a/src/Data.h b/src/Data.h new file mode 100644 index 0000000..96c9c96 --- /dev/null +++ b/src/Data.h @@ -0,0 +1,55 @@ +/** + * @file Data.h + * @date 23.01.2021 + * @author Grandeur Technologies + * + * Copyright (c) 2019 Grandeur Technologies LLP. All rights reserved. + * This file is part of the Arduino SDK for Grandeur. + * + */ + +// Including headers +#include "grandeurtypes.h" +#include "grandeurmacros.h" +#include "DuplexHandler.h" + +#ifndef DATA_H_ +#define DATA_H_ + +class Event { + // Class will help in clearing an event + private: + DuplexHandler _duplex; + String _deviceID; + gID _id; + String _path; + + public: + // Event constructor + Event(String deviceID, DuplexHandler duplexHandler, gID id, String path); + Event(); + + // Clear method + void clear(); +}; + +class Data { + // Class for handling device related functions + private: + DuplexHandler _duplex; + String _deviceID; + + public: + // Data constructor + Data(String deviceID, DuplexHandler duplexHandler); + Data(); + + // Async getter/setter methods + void get(const char* path, Callback callback); + void set(const char* path, Var data, Callback callback); + + // Sync event handlers + Event on(const char* path, Callback callback); +}; + +#endif \ No newline at end of file diff --git a/src/Datastore.cpp b/src/Datastore.cpp index f5b98cb..b5167c4 100644 --- a/src/Datastore.cpp +++ b/src/Datastore.cpp @@ -12,119 +12,169 @@ #include "Datastore.h" Datastore::Datastore(DuplexHandler duplexHandler) { + // Store reference to duplex into context _duplex = duplexHandler; } -Datastore::Datastore() {} +Datastore::Datastore() { + // Default constructor +} Collection Datastore::collection(String name) { + // Return a reference to collection object return Collection(name, _duplex); } Collection::Collection(String name, DuplexHandler duplexHandler) { + // Store name of collection and reference to object _name = name; _duplex = duplexHandler; } -void Collection::insert(JSONObject documents, Callback inserted) { - JSONObject jsonObject; +void Collection::insert(Var documents, Callback inserted) { + // Insert documents to datastore + Var jsonObject; + + // Define string char jsonString[PACKET_SIZE]; + // Append collection name and documents jsonObject["collection"] = _name; jsonObject["documents"] = documents; + // Convert to string JSON.stringify(jsonObject).toCharArray(jsonString, PACKET_SIZE); + // Send request to server _duplex.send("/datastore/insert", jsonString, inserted); } -void Collection::remove(JSONObject filter, Callback removed) { - JSONObject jsonObject; - char jsonString[PACKET_SIZE]; +void Collection::remove(Var filter, Callback removed) { + // Remove documents from datastore + Var jsonObject; + // Define string + char jsonString[PACKET_SIZE]; + + // Append collection name and filter jsonObject["collection"] = _name; jsonObject["filter"] = filter; - - JSON.stringify(jsonObject).toCharArray(jsonString, PACKET_SIZE); + // Convert to string + JSON.stringify(jsonObject).toCharArray(jsonString, PACKET_SIZE); + + // Send request to server _duplex.send("/datastore/delete", jsonString, removed); } -void Collection::update(JSONObject filter, JSONObject update, Callback updated) { - JSONObject jsonObject; - char jsonString[PACKET_SIZE]; +void Collection::update(Var filter, Var update, Callback updated) { + // Update document from datastore + Var jsonObject; + // Define string + char jsonString[PACKET_SIZE]; + + // Append collection name, filter and update jsonObject["collection"] = _name; jsonObject["filter"] = filter; - jsonObject["update"] = update; - + + // Convert to string JSON.stringify(jsonObject).toCharArray(jsonString, PACKET_SIZE); + // Send request to server _duplex.send("/datastore/update", jsonString, updated); } -void Collection::search(JSONObject filter, JSONObject projection, int pageNumber, Callback searched) { +void Collection::search(Var filter, Var projection, int nPage, Callback searched) { + // Basically it will use pipeline Pipeline searchPipeline = Pipeline(_name, {}, _duplex).match(filter); - if(projection == undefined) {} + + // Add project stage if provided + if(projection == undefined); else { searchPipeline = searchPipeline.project(projection); } - return searchPipeline.execute(pageNumber, searched); + // Execute the pipeline + return searchPipeline.execute(nPage, searched); } Pipeline Collection::pipeline(void) { + // Return a reference to pipeline return Pipeline(_name, undefined, _duplex); } -Pipeline::Pipeline(String collectionName, JSONObject query, DuplexHandler duplexHandler) { - _collection = collectionName; +Pipeline::Pipeline(String collection, Var query, DuplexHandler duplexHandler) { + // Setup the context + _collection = collection; _query = query; _duplex = duplexHandler; } -Pipeline Pipeline::match(JSONObject filter) { +Pipeline Pipeline::match(Var filter) { + // Add match stage to the pipeline int stage = _query.length() + 1; + + // Add type and filter _query[stage]["type"] = "match"; _query[stage]["filter"] = filter; + // Return reference to pipeline to basically help in chaining return Pipeline(_collection, _query, _duplex); } -Pipeline Pipeline::project(JSONObject specs) { +Pipeline Pipeline::project(Var specs) { + // Add project stage to the pipeline int stage = _query.length() + 1; - _query[stage]["type"] = "project"; + + // Add type and specs + _query[stage]["type"] = "match"; _query[stage]["specs"] = specs; + // Return reference to pipeline to basically help in chaining return Pipeline(_collection, _query, _duplex); } -Pipeline Pipeline::group(JSONObject condition, JSONObject fields) { +Pipeline Pipeline::group(Var condition, Var fields) { + // Add group stage to the pipeline int stage = _query.length() + 1; - _query[stage]["type"] = "group"; + + // Add type, condition and fields + _query[stage]["type"] = "match"; _query[stage]["condition"] = condition; _query[stage]["fields"] = fields; + // Return reference to pipeline to basically help in chaining return Pipeline(_collection, _query, _duplex); } -Pipeline Pipeline::sort(JSONObject specs) { +Pipeline Pipeline::sort(Var specs) { + // Add sort stage to the pipeline int stage = _query.length() + 1; - _query[stage]["type"] = "sort"; + + // Add type and specs + _query[stage]["type"] = "match"; _query[stage]["specs"] = specs; + // Return reference to pipeline to basically help in chaining return Pipeline(_collection, _query, _duplex); } -void Pipeline::execute(int pageNumber, Callback executed) { - JSONObject jsonObject; +void Pipeline::execute(int nPage, Callback executed) { + // Define an object + Var jsonObject; + + // Define string char jsonString[PACKET_SIZE]; + // Formulate query jsonObject["collection"] = _collection; jsonObject["pipeline"] = _query; - jsonObject["pageNumber"] = pageNumber; + jsonObject["nPage"] = nPage; + // Convert query to string JSON.stringify(jsonObject).toCharArray(jsonString, PACKET_SIZE); + // Send to server _duplex.send("/datastore/pipeline", jsonString, executed); } diff --git a/src/Datastore.h b/src/Datastore.h index a6a42ee..74cf481 100644 --- a/src/Datastore.h +++ b/src/Datastore.h @@ -17,45 +17,76 @@ class Pipeline { // Class for a datastore collection private: + // Dedine collection name String _collection; - JSONObject _query; + + // Variable to store query + Var _query; + + // To store reference of duplex DuplexHandler _duplex; + public: // Collection constructor - Pipeline(String collectionName, JSONObject query, DuplexHandler duplexHandler); - // Methods - Pipeline match(JSONObject filter); - Pipeline project(JSONObject specs); - Pipeline group(JSONObject condition, JSONObject fields); - Pipeline sort(JSONObject specs); - void execute(int pageNumber, Callback executed); + Pipeline(String collection, Var query, DuplexHandler duplexHandler); + + // Method to add match stage + Pipeline match(Var filter); + + // Function to add project stage + Pipeline project(Var specs); + + // Function to add group stage + Pipeline group(Var condition, Var fields); + + // Function to add sort stage to query + Pipeline sort(Var specs); + + // Execute the query by sending function + void execute(int nPage, Callback executed); }; class Collection { // Class for a datastore collection private: + // Define name of collection String _name; + + // Store duplex handler reference DuplexHandler _duplex; + public: // Collection constructor Collection(String name, DuplexHandler duplexHandler); - // Methods - void insert(JSONObject documents, Callback inserted); - void remove(JSONObject filter, Callback removed); - void update(JSONObject filter, JSONObject update, Callback updated); - void search(JSONObject filter, JSONObject projection, int pageNumber, Callback searched); + + // Function to insert document to a collection + void insert(Var documents, Callback inserted); + + // Function to remove document from a collection + void remove(Var filter, Callback removed); + + // Function to update a document + void update(Var filter, Var update, Callback updated); + + // Function to perform a search + void search(Var filter, Var projection, int nPage, Callback searched); + + // Constructor Pipeline pipeline(void); }; class Datastore { // Class for handling datastore related functions private: + // Variable to store duplex reference DuplexHandler _duplex; + public: // Datastore constructor Datastore(DuplexHandler duplexHandler); Datastore(); - // Methods + + // Method to get reference to constructor Collection collection(String name); }; diff --git a/src/Device.cpp b/src/Device.cpp index fbfa521..af911be 100644 --- a/src/Device.cpp +++ b/src/Device.cpp @@ -10,6 +10,7 @@ #include "Device.h" +// Constructor Device::Device(String deviceID, DuplexHandler duplexHandler) { _duplex = duplexHandler; _deviceID = deviceID; @@ -17,54 +18,8 @@ Device::Device(String deviceID, DuplexHandler duplexHandler) { Device::Device() {} -void Device::getSummary(Callback callback) { - JSONObject jsonObject; - char jsonString[PACKET_SIZE]; - jsonObject["deviceID"] = _deviceID; - JSON.stringify(jsonObject).toCharArray(jsonString, PACKET_SIZE); - _duplex.send("/device/summary/get", jsonString, callback); -} - -void Device::getParms(Callback callback) { - JSONObject jsonObject; - char jsonString[PACKET_SIZE]; - jsonObject["deviceID"] = _deviceID; - JSON.stringify(jsonObject).toCharArray(jsonString, PACKET_SIZE); - _duplex.send("/device/parms/get", jsonString, callback); -} - -void Device::setSummary(JSONObject summary, Callback callback) { - JSONObject jsonObject; - char jsonString[PACKET_SIZE]; - jsonObject["deviceID"] = _deviceID; - jsonObject["summary"] = summary; - JSON.stringify(jsonObject).toCharArray(jsonString, PACKET_SIZE); - _duplex.send("/device/summary/set", jsonString, callback); -} - -void Device::setParms(JSONObject parms, Callback callback) { - JSONObject jsonObject; - char jsonString[PACKET_SIZE]; - jsonObject["deviceID"] = _deviceID; - jsonObject["parms"] = parms; - JSON.stringify(jsonObject).toCharArray(jsonString, PACKET_SIZE); - _duplex.send("/device/parms/set", jsonString, callback); -} - -void Device::onSummary(Callback updateHandler) { - JSONObject jsonObject; - char jsonString[PACKET_SIZE]; - jsonObject["deviceID"] = _deviceID; - jsonObject["event"] = "deviceSummary"; - JSON.stringify(jsonObject).toCharArray(jsonString, PACKET_SIZE); - _duplex.subscribe(SUMMARYUPDATE, jsonString, updateHandler); -} - -void Device::onParms(Callback updateHandler) { - JSONObject jsonObject; - char jsonString[PACKET_SIZE]; - jsonObject["deviceID"] = _deviceID; - jsonObject["event"] = "deviceParms"; - JSON.stringify(jsonObject).toCharArray(jsonString, PACKET_SIZE); - _duplex.subscribe(PARMSUPDATE, jsonString, updateHandler); +// Return reference to data +Data Device::data() { + // Return new device class object + return Data(_deviceID, _duplex); } \ No newline at end of file diff --git a/src/Device.h b/src/Device.h index e64fd26..58c3e90 100644 --- a/src/Device.h +++ b/src/Device.h @@ -12,6 +12,7 @@ #include "grandeurtypes.h" #include "grandeurmacros.h" #include "DuplexHandler.h" +#include "Data.h" #ifndef DEVICE_H_ #define DEVICE_H_ @@ -19,22 +20,17 @@ class Device { // Class for handling device related functions private: + // Store duplex and device id DuplexHandler _duplex; String _deviceID; + public: // Device constructor Device(String deviceID, DuplexHandler duplexHandler); Device(); - // Async getter/setter methods - void getSummary(Callback callback); - void getParms(Callback callback); - void setSummary(JSONObject summary, Callback callback); - void setParms(JSONObject parms, Callback callback); - - // Listeners for events from Grandeur - void onSummary(Callback callback); - void onParms(Callback callback); + // Instantiator methods - return the objects of their classes + Data data(); }; #endif \ No newline at end of file diff --git a/src/DuplexHandler.cpp b/src/DuplexHandler.cpp index a2a904b..9688ec8 100644 --- a/src/DuplexHandler.cpp +++ b/src/DuplexHandler.cpp @@ -11,27 +11,34 @@ #include "DuplexHandler.h" #include "arduinoWebSockets/WebSockets.h" -/* VARIABLE INITIALIZATIONS */ +/* Create client */ WebSocketsClient client; + +// Init ping counter unsigned long millisCounterForPing = 0; -const char* subscriptionTopics[] = {"deviceSummary", "deviceParms"}; -size_t sendQueueSize = 0; -SendData* sendQueue[SENDQUEUE_SIZE] = {}; + +// Init status handler and variable short DuplexHandler::_status = DISCONNECTED; void (*DuplexHandler::_connectionCallback)(bool) = [](bool connectionEventHandler) {}; +// Define a queue +EventQueue DuplexHandler::_queue; + +// Create a new event table and subscriptions EventTable DuplexHandler::_eventsTable; -Callback DuplexHandler::_subscriptions[4] = {}; +EventTable DuplexHandler::_subscriptions; -/* EVENT HANDLER FUNCTIONS */ +/* Deifne event handler */ void duplexEventHandler(WStype_t eventType, uint8_t* packet, size_t length); DuplexHandler::DuplexHandler(Config config) { + // Constructor _query = "/?type=device&apiKey=" + config.apiKey; _token = config.token; } DuplexHandler::DuplexHandler() { + // Overload the contructor _query = "/?type=device"; _token = ""; } @@ -39,24 +46,31 @@ DuplexHandler::DuplexHandler() { void DuplexHandler::init(void) { // Setting up event handler client.onEvent(&duplexEventHandler); + // Scheduling reconnect every 5 seconds if it disconnects client.setReconnectInterval(5000); + // Opening up the connection client.beginSSL(GRANDEUR_URL, GRANDEUR_PORT, _query, GRANDEUR_FINGERPRINT, "node"); + + // Set auth header char tokenArray[_token.length() + 1]; _token.toCharArray(tokenArray, _token.length() + 1); client.setAuthorization(tokenArray); } void DuplexHandler::onConnectionEvent(void connectionEventHandler(bool)) { + // Event handler for connection _connectionCallback = connectionEventHandler; } short DuplexHandler::getStatus() { + // Return status return _status; } void DuplexHandler::loop(bool valve) { + // Give duplex time to execute if(valve) { // If valve is true => valve is open if(millis() - millisCounterForPing >= PING_INTERVAL) { @@ -64,42 +78,155 @@ void DuplexHandler::loop(bool valve) { millisCounterForPing = millis(); ping(); } + // Running duplex loop client.loop(); } } +// Define the handle function +void DuplexHandler::handle(EventID id, EventKey key, EventPayload payload, Callback callback) { + // Check connection status + if(_status != CONNECTED) { + return ; + } + + // Create packet + char packet[PACKET_SIZE]; + + // Saving callback to eventsTable + _eventsTable.insert(key, id, callback); + // _eventsTable.print(); + // _subscriptions.print(); + + // Formatting the packet + snprintf(packet, PACKET_SIZE, "{\"header\": {\"id\": %lu, \"task\": \"%s\"}, \"payload\": %s}", id, key.c_str(), payload.c_str()); + + // Sending to server + client.sendTXT(packet); +} + void DuplexHandler::send(const char* task, const char* payload, Callback callback) { + // Generate packet id + gID packetID = micros(); + + // Check connection status if(_status != CONNECTED) { - sendQueue[sendQueueSize++] = new SendData(task, payload, callback); + // Append the packet to queue + _queue.push(packetID, task, payload, callback); + // _queue.print(); return ; } + + // Create packet char packet[PACKET_SIZE]; - GrandeurID packetID = millis(); + // Saving callback to eventsTable _eventsTable.insert(task, packetID, callback); + // _eventsTable.print(); + // _subscriptions.print(); + // Formatting the packet snprintf(packet, PACKET_SIZE, "{\"header\": {\"id\": %lu, \"task\": \"%s\"}, \"payload\": %s}", packetID, task, payload); + // Sending to server client.sendTXT(packet); } -void DuplexHandler::subscribe(short event, const char* payload, Callback updateHandler) { - // Saving updateHandler callback to subscriptions Array - _subscriptions[event] = updateHandler; - send("/topic/subscribe", payload, [](JSONObject payload) {}); +void DuplexHandler::unsubscribe(gID id, const char* payload) { + // Generate an id + gID packetID = micros(); + + // Remove callback from subscriptions table + _subscriptions.remove(id); + // _eventsTable.print(); + // _subscriptions.print(); + + // Push unsub to queue + _queue.push(packetID, "/topic/unsubscribe", payload, NULL); + + // and remove subscription packet from queue + _queue.remove(id); + // _queue.print(); + + // Check connection status + if(_status != CONNECTED) { + // Don't proceed if we aren't + return ; + } + + // Create packet + char packet[PACKET_SIZE]; + + // Saving callback to eventsTable + _eventsTable.insert("/topic/unsubscribe", packetID, NULL); + // _eventsTable.print(); + // _subscriptions.print(); + + // Formatting the packet + snprintf(packet, PACKET_SIZE, "{\"header\": {\"id\": %lu, \"task\": \"%s\"}, \"payload\": %s}", packetID, "/topic/unsubscribe", payload); + + // Sending to server + client.sendTXT(packet); +} + +gID DuplexHandler::subscribe(const char* event, const char* payload, Callback updateHandler) { + // Generate an id + gID packetID = micros(); + + // Saving callback to subscriptions Table + _subscriptions.insert(event, packetID, updateHandler); + // _eventsTable.print(); + // _subscriptions.print(); + + // Append the packet to queue because in case of queue + // the packet will always be queue either we are connected + // or disconnected + // This is being done to handle case where we were connected + // the subscribed to some events and got disconnected + _queue.push(packetID, "/topic/subscribe", payload, NULL); + // _queue.print(); + + // Check connection status + if(_status != CONNECTED) { + // Don't proceed if we aren't + // but return packet id + return packetID; + } + + // Create packet + char packet[PACKET_SIZE]; + + // Saving callback to eventsTable + _eventsTable.insert("/topic/subscribe", packetID, NULL); + // _eventsTable.print(); + // _subscriptions.print(); + + // Formatting the packet + snprintf(packet, PACKET_SIZE, "{\"header\": {\"id\": %lu, \"task\": \"%s\"}, \"payload\": %s}", packetID, "/topic/subscribe", payload); + + // Sending to server + client.sendTXT(packet); + + // Return packet id + return packetID; } -/** This function pings the cloud to keep the connection alive. -*/ void DuplexHandler::ping() { + // Ping handler if(_status == CONNECTED) { + // Generate packet char packet[PING_PACKET_SIZE]; - GrandeurID packetID = millis(); + + // Create id + gID packetID = millis(); + // Saving callback to eventsTable - _eventsTable.insert("ping", packetID, [](JSONObject feed) {}); + _eventsTable.insert("ping", packetID, NULL); + // Formatting the packet snprintf(packet, PING_PACKET_SIZE, "{\"header\": {\"id\": %lu, \"task\": \"ping\"}}", packetID); + // Sending to server client.sendTXT(packet); } @@ -111,53 +238,88 @@ void DuplexHandler::ping() { * @param length: The size of the @param packet. */ void duplexEventHandler(WStype_t eventType, uint8_t* packet, size_t length) { - JSONObject updateObject; + + // Switch over event type switch(eventType) { case WStype_CONNECTED: // When duplex connection opens DuplexHandler::_status = CONNECTED; + + // Generate callback DuplexHandler::_connectionCallback(DuplexHandler::_status); + // Resetting ping millis counter millisCounterForPing = millis(); - // Sending all queued messages - for(int i = 0; i < sendQueueSize; i++) { - DuplexHandler::send(sendQueue[i]->task, sendQueue[i]->payload, sendQueue[i]->callback); - } + + // Handle the queued events + DuplexHandler::_queue.forEach(DuplexHandler::handle); + + // Then send return ; case WStype_DISCONNECTED: // When duplex connection closes DuplexHandler::_status = DISCONNECTED; + + // Clear event table + DuplexHandler::_eventsTable.clear(); + + // Generate connection event return DuplexHandler::_connectionCallback(DuplexHandler::_status); case WStype_TEXT: + // Serial.printf("%s\n", packet); + // When a duplex message is received. - JSONObject messageObject = JSON.parse((char*) packet); + Var messageObject = JSON.parse((char*) packet); + + // Handle parsing errors if (JSON.typeof(messageObject) == "undefined") { // Just for internal errors of Arduino_JSON // if the parsing fails. DEBUG_GRANDEUR("Parsing input failed!"); return; } + + // If it is an update instead of response to a task if(messageObject["header"]["task"] == "update") { - for(int i = 0; i < NUMBER_OF_TOPICS; i++) { - if(messageObject["payload"]["event"] == subscriptionTopics[i]) { - DuplexHandler::_subscriptions[i](messageObject["payload"]["update"]); - return ; - } - } + // Formulate the key + std::string event((const char*) messageObject["payload"]["event"]); + std::string path((const char*) messageObject["payload"]["path"]); + + // Handle the backward compatibility case + if (event == "deviceParms" || event == "deviceSummary") event = "data"; + + // Emit the event + DuplexHandler::_subscriptions.emit(event + "/" + path, messageObject["payload"]["update"], messageObject["payload"]["path"]); + + // Return + return; } + // Just return if it is unpair event else if(messageObject["header"]["task"] == "unpair") { return ; } + // Fetching event callback function from the events Table - Callback callback = DuplexHandler::_eventsTable.findAndRemove( - (const char*) messageObject["header"]["task"], - (GrandeurID) messageObject["header"]["id"] - ); - if(!callback) { - return; + Callback callback = DuplexHandler::_eventsTable.findAndRemove((gID) messageObject["header"]["id"]); + + // DuplexHandler::_eventsTable.print(); + // DuplexHandler::_subscriptions.print(); + + // Remove the packet if it was queued + // because the ack has been received + if (messageObject["header"]["task"] == "/topic/subscribe"); + else { + // but if it is not of subscribe type + DuplexHandler::_queue.remove((long) messageObject["header"]["id"]); + // DuplexHandler::_queue.print(); } + + // If not found then simply return + if(!callback) return; + + // Or otherwise resolve the event return callback(messageObject["payload"]); } } diff --git a/src/DuplexHandler.h b/src/DuplexHandler.h index 7f14bc0..c46d523 100644 --- a/src/DuplexHandler.h +++ b/src/DuplexHandler.h @@ -10,6 +10,7 @@ // Including headers #include "EventTable.h" +#include "EventQueue.h" #include "grandeurtypes.h" #include "grandeurmacros.h" #include "arduinoWebSockets/WebSocketsClient.h" @@ -21,28 +22,48 @@ class DuplexHandler { private: // Connection query String _query; + // Connection token String _token; + // Connection state variable static short _status; + + // Event Queue + static EventQueue _queue; + // Events Table static EventTable _eventsTable; - // Subscription Array for update handler functions - static Callback _subscriptions[4]; + + // Subscriptions + static EventTable _subscriptions; // Container for connection callback static void (*_connectionCallback)(bool); + // Define function to handle the queued events + static void handle(EventID id, EventKey key, EventPayload payload, Callback callback); + public: + // Constructor DuplexHandler(Config config); DuplexHandler(); + + // Init the connection void init(void); + // Ping function to keep connection alive. void ping(); + // Function to send a generic duplex message. static void send(const char* task, const char* payload, Callback callback); + + // Function to unsubscribe to a device topic. + void unsubscribe(gID id, const char* payload); + // Function to subscribe to a device topic. - void subscribe(short event, const char* payload, Callback updateHandler); + gID subscribe(const char* event, const char* payload, Callback updateHandler); + // Function to schedule an event handler for connection with Grandeur void onConnectionEvent(void connectionEventHandler(bool)); @@ -53,7 +74,6 @@ class DuplexHandler { void loop(bool valve); friend void duplexEventHandler(WStype_t eventType, uint8_t* packet, size_t length); - }; #endif \ No newline at end of file diff --git a/src/EventQueue.cpp b/src/EventQueue.cpp new file mode 100644 index 0000000..f1a1de2 --- /dev/null +++ b/src/EventQueue.cpp @@ -0,0 +1,172 @@ +/** + * @file EventQueue.cpp + * @date 24.03.2020 + * @author Grandeur Technologies + * + * Copyright (c) 2019 Grandeur Technologies LLP. All rights reserved. + * This file is part of the Arduino SDK for Grandeur. + * + */ + +// Inlcude event table +#include +#include + +// Implement event queue block +EventQueueEntry::EventQueueEntry(EventID id, EventKey key, EventPayload payload, Callback callback) { + this->id = id; + this->key = key; + this->payload = payload; + this->callback = callback; + this->next = NULL; +} + +EventQueueEntry::EventQueueEntry(int k) { + this->id = NULL; +} + +bool EventQueueEntry::operator!() { + // If the queue block was not set as null + // then return true + if (id != NULL) { + return true; + } + + // Otherwise return false + return false; +} + +// Implement event queue constructor +EventQueue::EventQueue() { + // Init queue as null + queue = NULL; +} + +int EventQueue::push(EventID id, EventKey key, EventPayload payload, Callback callback) { + // We will insert a block at the head + // Store the reference to next of queue + EventQueueEntry* p = queue; + + // Then assign the queue to new block + queue = new EventQueueEntry(id, key, payload, callback); + + // Then assign queue next of old block + queue->next = p; + + // Plus the length + _length++; + + return 0; +} + +int EventQueue::remove(EventID id) { + // Check if bucket is empty + if(queue == NULL) { + // which means the entry does not exist + return -1; + } + + // If the id was found in first block + if(queue->id == id) { + // Pull the chain backwards + EventQueueEntry* p = queue; + queue = queue->next; + + // and delete it + delete p; + return 0; + } + + // If the bucket is not empty, + // getting ready for traversing the chain + EventQueueEntry* p = queue; + + // While not the end has reached + while (p->next != NULL) { + // Break if the next block has the id we need + if (p->next->id == id) break; + + // Keep moving + p = p->next; + } + + // Handle case where we reached the end + if (p->next == NULL) { + // No element is found + return -1; + } + + // If the element is found + // Then pull the chain backward + EventQueueEntry* q = p->next; + p->next = p->next->next; + + // and delete it + delete q; + return 0; +} + +void EventQueue::forEach(void (*callback)(EventID, EventKey, EventPayload, Callback)) { + // Loop over the queue + EventQueueEntry* p = queue; + + // While not the end + while (p != NULL) { + // Call the callback + callback(p->id, p->key, p->payload, p->callback); + + // Move to next + p = p->next; + } +} + +int EventQueue::length() { + // Return length of queue + return _length; +} + +void EventQueue::print() { + // Loop over the queue + EventQueueEntry* p = queue; + + // Print header + Serial.printf("\n*******Queue*******\n"); + + // While not the end + while (p != NULL) { + // Print the id + Serial.printf("%s %d\n", p->key.c_str(), p->id); + + p = p->next; + } + + // Print footer + Serial.printf("*******************\n"); +} + +void EventQueue::clear() { + // Function to delete all blocks + // If the bucket is not empty, + // getting ready for traversing the chain + EventQueueEntry* p = queue; + + // While not the end has reached + while (p != NULL) { + // Copy block address to a pointer + EventQueueEntry* next = p->next; + + // delete p + delete p; + + // Move next + p = next; + } + + // Mark queue as empty + queue = NULL; + _length = 0; +} + +EventQueue::~EventQueue() { + clear(); +} \ No newline at end of file diff --git a/src/EventQueue.h b/src/EventQueue.h new file mode 100644 index 0000000..af3d04c --- /dev/null +++ b/src/EventQueue.h @@ -0,0 +1,72 @@ +/** + * @file EventQueue.h + * @date 29.01.2021 + * @author Grandeur Technologies + * + * Copyright (c) 2019 Grandeur Technologies LLP. All rights reserved. + * This file is part of the Arduino SDK for Grandeur. + * + */ + +#include +#include +#include "grandeurtypes.h" + +#ifndef EVENTQUEUE_H_ +#define EVENTQUEUE_H_ + +// Event table entry is the block which represents +// an event +class EventQueueEntry { + public: + EventID id; + EventKey key; + EventPayload payload; + Callback callback; + EventQueueEntry* next; + + // Constructor + EventQueueEntry(EventID id, EventKey key, EventPayload payload, Callback callback); + EventQueueEntry(int k); + + // Override bool operator not + bool operator!(); +}; + +// The main class +class EventQueue { + private: + // Define queue + EventQueueEntry *queue; + + // Queue length + int _length = 0; + + public: + // Constructor for a table + EventQueue(); + + // Method to insert an entry into the queue + int push(EventID id, EventKey key, EventPayload payload, Callback callback); + + // Method to remove an entry + int remove(EventID id); + + // Method to loop over the elements in queue + // and return the entry in callback + void forEach(void (*callback)(EventID, EventKey, EventPayload, Callback)); + + // Function return queue size + int length(); + + // Print queue + void print(); + + // Method to clear queue + void clear(); + + // Destructor for a queue + ~EventQueue(); +}; + +#endif \ No newline at end of file diff --git a/src/EventTable.cpp b/src/EventTable.cpp index d592bbe..b9b5d0c 100644 --- a/src/EventTable.cpp +++ b/src/EventTable.cpp @@ -8,167 +8,272 @@ * */ +// Inlcude event table #include #include +#include -const char* routes[T_S] = - {"ping", "/topic/subscribe", "/topic/unsubscribe", "/topics/unsubscribe", - "/device/summary/get", "/device/parms/get", "/device/summary/set", "/device/parms/set", - "/datastore/insert", "/datastore/delete", "/datastore/update", "/datastore/pipeline"}; - -EventTableEntry::EventTableEntry(EventTableKey k, EventTableData v) { - this->k= k; - this->v = v; +// Implement event table block +EventTableEntry::EventTableEntry(EventKey key, EventID id, EventData data) { + this->key = key; + this->id = id; + this->data = data; this->next = NULL; } +// Implement event table constructor EventTable::EventTable() { - t = new EventTableEntry * [T_S]; - for (int i = 0; i < T_S; i++) { - t[i] = NULL; - } + // Init table as null + table = NULL; } -int EventTable::hashFunc(EventTableKey k) { - for(int i = 0; i < T_S; i++) { - if(routes[i] == k) { - return i; - } - } - return (T_S - 1); -} - -int EventTable::insert(EventTableKey k, EventID id, EventData data) { - int h = hashFunc(k); - if(t[h] == NULL) { - // If this bucket is empty, +int EventTable::insert(EventKey key, EventID id, EventData data) { + // If bucket is empty, + if(table == NULL) { // creating the first entry - t[h] = new EventTableEntry(k, {id, data}); + table = new EventTableEntry(key, id, data); return 0; } - if(t[h]->v.id == id) { + + // Otherwise resolve the base case where the starting block id + // is duplicate of the new supllied + if(table->id == id) { // If entry is found in the first element, // replacing its data - t[h]->v.data = data; + table->data = data; return 0; } + // If the bucket is not empty, // getting ready for traversing the chain - EventTableEntry* p = t[h]; - while (p->next != NULL && p->next->v.id != id) { + EventTableEntry * p = table; + + // Move to next block while we aren't at the end + while (p->next != NULL) { + // or we haven't found a duplicate in next block + if (p->next->id == id) break; + + // Keep moving p = p->next; } + + // When found a duplicate if (p->next != NULL) { - // If found an index without reaching the end of chain, + // Without reaching the end of chain, // just replace the data - p->next->v.data = data; + p->next->data = data; return 0; } + // If the end of chain is reached, // creating a new entry in the chain - p->next = new EventTableEntry(k, {id, data}); + p->next = new EventTableEntry(key, id, data); return 0; } -int EventTable::remove(EventTableKey k, EventID id) { - int h = hashFunc(k); - if(t[h] == NULL) { - // If this bucket is empty, +int EventTable::remove(EventID id) { + // Check if bucket is empty + if(table == NULL) { // which means the entry does not exist return -1; } - if(t[h]->v.id == id) { - // If entry is found in the first element, - // deleting it and pulling the chain backwards - EventTableEntry* p = t[h]; - t[h] = t[h]->next; + + // If the id was found in first block + if(table->id == id) { + // Pull the chain backwards + EventTableEntry* p = table; + table = table->next; + + // and delete it delete p; return 0; } + // If the bucket is not empty, // getting ready for traversing the chain - EventTableEntry* p = t[h]; + EventTableEntry* p = table; + + // While not the end has reached while (p->next != NULL) { - if (p->next->v.id == id) - // If element is found, - // breaking out of the loop - break; + // Break if the next block has the id we need + if (p->next->id == id) break; + + // Keep moving p = p->next; } + + // Handle case where we reached the end if (p->next == NULL) { - // If the end of the chain is reached, - // and no element is found + // No element is found return -1; } + // If the element is found + // Then pull the chain backward EventTableEntry* q = p->next; p->next = p->next->next; + + // and delete it delete q; return 0; } -EventData EventTable::findAndRemove(EventTableKey k, EventID id) { - int h = hashFunc(k); - if(t[h] == NULL) { - // If this bucket is empty, +EventData EventTable::findAndRemove(EventID id) { + // Check if bucket is empty + if(table == NULL) { // which means the entry does not exist return NULL; } - if(t[h]->v.id == id) { - // If entry is found in the first element, - // copying it, deleting it and pulling the chain backwards - EventTableEntry* p = t[h]; - EventData data = p->v.data; - t[h] = t[h]->next; + + // If the id was found in first block + if(table->id == id) { + // Pull the chain backwards + EventTableEntry* p = table; + EventData data = p->data; + table = table->next; + + // and delete it delete p; return data; } + // If the bucket is not empty, // getting ready for traversing the chain - EventTableEntry* p = t[h]; + EventTableEntry* p = table; + + // While not the end has reached while (p->next != NULL) { - if (p->next->v.id == id) - // If element is found, - // breaking out of the loop - break; + // Break if the next block has the id we need + if (p->next->id == id) break; + + // Keep moving p = p->next; } + + // Handle case where we reached the end if (p->next == NULL) { - // If the end of the chain is reached, - // and no element is found + // No element is found return NULL; } + // If the element is found - // copying its data, deleting it and pulling the chain backwards + // Then pull the chain backward EventTableEntry* q = p->next; - EventData data = q->v.data; + EventData data = p->data; p->next = p->next->next; + + // and delete it delete q; return data; } -/* ONLY FOR PRINTABLE DATA TYPES */ -void EventTable::print() { - for(int i = 0; i < T_S; i++) { - EventTableEntry* p = t[i]; - std::cout< "; - while (p != NULL) { - std::cout<k<<" : "; - std::cout<v.id<< " : "; - //std::cout<v.data<<" -> "; - p = p->next; +int EventTable::emit(EventKey key, Var packet, const char* path) { + // Cast packet as per type of packet + String type = JSON.typeof_(packet); + + // If the bucket is not empty, + // getting ready for traversing the chain + EventTableEntry* p = table; + + // While not the end has reached + while (p != NULL) { + // Define pattern + std::regex pattern("(" + p->key + ")(.*)"); + + // We will use regex to match the key of the block + if (std::regex_match(key, pattern)) { + // Then emit packet to callback + // Check the callback type + if (p->data.type() == "object") { + // The callback receives an object + // so we don't to do the type conversion + // Send with casting as Var + p->data((Var) packet, path); + } + else { + // Packet was boolean + if (type == "boolean") { + // Cast as boolean + p->data((bool) packet, path); + + } + + // Packet is number + else if (type == "number") { + // Figure out that if it is a double or int + if ((double) packet - (int) packet != 0) + // Cast as double + p->data((double) packet, path); + + else + // Cast as int + p->data((int) packet, path); + + } + + // Packet was string + else if (type == "string") { + // Cast as string + p->data((const char*) packet, path); + + } + + // Packet was object or array + else if (type == "object" || type == "array") { + // Send with casting as Var + p->data((Var) packet, path); + + } + } } - std::cout<<"\n"; + // Keep moving + p = p->next; } + + return 0; } -EventTable::~EventTable() { - for (int i = 0; i < T_S; i++) { - if (t[i] != NULL) - for(EventTableEntry* p = t[i]; p != NULL; p = p->next) { - delete p; - t[i] = NULL; - } +void EventTable::print() { + // Loop over the event table + EventTableEntry* p = table; + + // Print header + Serial.printf("\n*******Table*******\n"); + + // While not the end + while (p != NULL) { + // Print the id + Serial.printf("%s %d\n", p->key.c_str(), p->id); + + p = p->next; } - delete[] t; + + // Print footer + Serial.printf("*******************\n"); +} + +void EventTable::clear() { + // Function to delete all blocks + // If the bucket is not empty, + // getting ready for traversing the chain + EventTableEntry* p = table; + + // While not the end has reached + while (p != NULL) { + // Copy block address to a pointer + EventTableEntry* next = p->next; + + // delete p + delete p; + + // Move next + p = next; + } + + // Mark table as empty + table = NULL; +} + +EventTable::~EventTable() { + clear(); } \ No newline at end of file diff --git a/src/EventTable.h b/src/EventTable.h index 4ddd058..672a273 100644 --- a/src/EventTable.h +++ b/src/EventTable.h @@ -15,49 +15,47 @@ #ifndef EVENTTABLE_H_ #define EVENTTABLE_H_ -/*TYPEDEFS*/ -// EventID -typedef GrandeurID EventID; -//EventData -typedef Callback EventData; -// Event table data -typedef struct { - EventID id; - EventData data; -} EventTableData; -// Event table key -typedef std::string EventTableKey; - -// Event table size -const int T_S = 16; - -// Routes Array -extern const char* routes[T_S]; - -// Event table entry +// Event table entry is the block which represents +// an event class EventTableEntry { public: - EventTableKey k; - EventTableData v; + EventKey key; + EventID id; + EventData data; EventTableEntry* next; - EventTableEntry(EventTableKey k, EventTableData v); + + // Constructor + EventTableEntry(EventKey key, EventID id, EventData data); }; +// The main class class EventTable { private: - EventTableEntry **t; + // Define table + EventTableEntry *table; + public: - // Constructor for a hashtable + // Constructor for a table EventTable(); - // Hash function for Event Table - int hashFunc(EventTableKey k); + // Method to insert an entry into the hashtable - int insert(EventTableKey k, EventID id, EventData data); - // Method to remove an entry from the hashtable - int remove(EventTableKey k, EventID id); + int insert(EventKey key, EventID id, EventData data); + + // Method to remove an entry from the table + int remove(EventID id); + // Method to find and remove an entry from the hashtable - EventData findAndRemove(EventTableKey k, EventID id); + EventData findAndRemove(EventID id); + + // Method to send some data to all callbacks at a key + int emit(EventKey key, Var packet, const char* path); + + // Print table entreis void print(); + + // Clear event table entries + void clear(); + // Destructor for a hashtable ~EventTable(); }; diff --git a/src/Grandeur.cpp b/src/Grandeur.cpp index 1cc2068..accd097 100644 --- a/src/Grandeur.cpp +++ b/src/Grandeur.cpp @@ -18,30 +18,39 @@ Grandeur::Grandeur() {} Project Grandeur::init(String apiKey, String token) { // Setting config _config = {apiKey, token}; + // Creating a new project reference. Project project; + // Duplex handles the realtime connection with the project. project._duplexHandler = DuplexHandler(_config); + // Starting Duplex. project._duplexHandler.init(); + + // Return project object return project; } Project::Project() {} void Project::onConnection(void connectionCallback(bool)) { + // Connection handler for duplex _duplexHandler.onConnectionEvent(connectionCallback); } bool Project::isConnected(void) { + // Return status of duplex return _duplexHandler.getStatus() == CONNECTED; } Device Project::device(String deviceID) { + // Return new device class object return Device(deviceID, _duplexHandler); } Datastore Project::datastore(void) { + // Return new datastore class object return Datastore(_duplexHandler); } diff --git a/src/Grandeur.h b/src/Grandeur.h index 058f935..735cd4c 100644 --- a/src/Grandeur.h +++ b/src/Grandeur.h @@ -22,12 +22,15 @@ class Project { // Class for handling a complete project private: DuplexHandler _duplexHandler; + public: // Project constructor Project(); + // Connection related methods void onConnection(void connectionCallback(bool)); bool isConnected(void); + // Instantiator methods - return the objects of their classes Device device(String deviceID); Datastore datastore(void); @@ -40,9 +43,14 @@ class Project { class Grandeur { private: + // Create new config object Config _config; + public: + // Constructor of Grandeur Grandeur(); + + // Function to init the connection Project init(String apiKey, String token); }; diff --git a/src/grandeurmacros.h b/src/grandeurmacros.h index bb2a11a..82361d8 100644 --- a/src/grandeurmacros.h +++ b/src/grandeurmacros.h @@ -32,17 +32,9 @@ #define PING_PACKET_SIZE 64 #define TASK_SIZE 32 -// Send Queue Size -#define SENDQUEUE_SIZE 16 - // Ping interval in milliseconds #define PING_INTERVAL 25000 // Macros for connection status #define DISCONNECTED false #define CONNECTED true - -// Indexes for update handler callbacks in subscriptions array -#define NUMBER_OF_TOPICS 2 -#define SUMMARYUPDATE 0 -#define PARMSUPDATE 1 diff --git a/src/grandeurtypes.cpp b/src/grandeurtypes.cpp index e520057..fa67d29 100644 --- a/src/grandeurtypes.cpp +++ b/src/grandeurtypes.cpp @@ -11,18 +11,14 @@ #include #include "Arduino.h" +// Define config class Config::Config(String apiKey, String token) { this->apiKey = apiKey; this->token = token; } +// Config override constructor Config::Config() { this->apiKey = ""; this->token = ""; -} - -SendData::SendData(const char* task, const char* payload, Callback callback) { - strcpy(this->task, task); - strcpy(this->payload, payload); - this->callback = callback; } \ No newline at end of file diff --git a/src/grandeurtypes.h b/src/grandeurtypes.h index 27fc39b..5e245bf 100644 --- a/src/grandeurtypes.h +++ b/src/grandeurtypes.h @@ -15,31 +15,236 @@ #ifndef GRANDEURTYPES_H_ #define GRANDEURTYPES_H_ -typedef JSONVar JSONObject; -typedef void (*Callback)(JSONObject); -typedef void (*Send)(const char* task, const char* payload, Callback callback); -typedef long GrandeurID; +// Include json +typedef JSONVar Var; -// Config class for grandeur configurations +// Define Callback using template +class Callback { + private: + // Create a private variable + void * _c; + + // To store type of the callback + int _type = 0; + + // Type to string + std::string mapType(int type) { + // Match type and return string + switch(type) { + // Handle cases + case 0: return "null"; + case 1: return "object"; + case 2: return "object"; + case 3: return "boolean"; + case 4: return "int"; + case 5: return "long"; + case 6: return "double"; + case 7: return "string"; + } + } + + // Function to generate an error + void error(int type) { + // Throw error + Serial.printf("[TYPE-ERROR] Was expecting %s and received %s\n", mapType(_type).c_str(), mapType(type).c_str()); + } + + public: + // Default constructor will init the pointer + Callback() { + // We will init the context + _c = NULL; + + // Set type to null + _type = 0; + } + + Callback(int ptr) { + // We will init the context + _c = NULL; + + // Set type to null + _type = 0; + } + + // For json callback + Callback(void (*c)(Var)) { + // We will store it in context + _c = (void *) c; + + // Set type to object + _type = 1; + } + + Callback(void (*c)(Var, const char*)) { + // We will store it in context + _c = (void *) c; + + // Set type to object & str + _type = 2; + } + + Callback(void (*c)(bool, const char*)) { + // We will store it in context + _c = (void *) c; + + // Set type to bool & str + _type = 3; + } + + Callback(void (*c)(int, const char*)) { + // We will store it in context + _c = (void *) c; + + // Set type to int & str + _type = 4; + } + + Callback(void (*c)(long, const char*)) { + // We will store it in context + _c = (void *) c; + + // Set type to long & str + _type = 5; + } + + Callback(void (*c)(double, const char*)) { + // We will store it in context + _c = (void *) c; + + // Set type to double & str + _type = 6; + } + + Callback(void (*c)(const char*, const char*)) { + // We will store it in context + _c = (void *) c; + + // Set type to str & str + _type = 7; + } + + // Function to return type of callback + std::string type() { + // Map type and return + return mapType(_type); + } + + // Then we will override the function call + // operator to pass data to callback that we stored + // in the context + void operator()(Var data) { + // Validate type + if (_type == 1) + // Cast as var function pointer only when function was accepting var + ((void (*)(Var)) _c)(data); + + else + // Invalid type + error(1); + } + + // Overload for with string + void operator()(Var data, const char* path) { + // Validate type + if (_type == 2) + // Cast as var function pointer only when function was accepting var + ((void (*)(Var, const char*)) _c)(data, path); + + else + // Invalid type + error(2); + } + + void operator()(bool data, const char* path) { + // Validate type + if (_type == 3) + // Cast as bool function pointer only when function was accepting bool + ((void (*)(bool, const char*)) _c)(data, path); + + else + // Invalid type + error(3); + } + + void operator()(int data, const char* path) { + // Validate type + if (_type == 4) + // Cast as int function pointer only when function was accepting int + ((void (*)(int, const char*)) _c)(data, path); + + else + // Invalid type + error(4); + } + + void operator()(long data, const char* path) { + // Validate type + if (_type == 5) + // Cast as long function pointer only when function was accepting long + ((void (*)(long, const char*)) _c)(data, path); + + else + // Invalid type + error(5); + } + + void operator()(double data, const char* path) { + // Validate type + if (_type == 6) + // Cast as double function pointer only when function was accepting double + ((void (*)(double, const char*)) _c)(data, path); + + else + // Invalid type + error(6); + } + + void operator()(const char* data, const char* path) { + // Validate type + if (_type == 7) + // Cast as string function pointer only when function was accepting string + ((void (*)(const char*, const char*)) _c)(data, path); + + else + // Invalid type + error(7); + } + + // Override bool operator not + bool operator!() { + // Return true if the callback is not set + if (!_c && _type == 0) return true; + else return false; + } +}; + +// Define Grandeur ID +typedef long gID; + +// EventID +typedef gID EventID; + +//EventData +typedef Callback EventData; + +// Event key +typedef std::string EventKey; + +// Event payload +typedef std::string EventPayload; + +// Event key +typedef std::string EventKey; + +// Config class for storing configurations of connection class Config { public: String apiKey; String token; + + // Constructor Config(String apiKey, String token); Config(); }; - -// Class for Send Queue Data -class SendData { - public: - char task[TASK_SIZE]; - char payload[PACKET_SIZE]; - Callback callback; - SendData( - const char* task, - const char* payload, - Callback callback - ); -}; - #endif \ No newline at end of file