added arduino, modified build
This commit is contained in:
32
arduino/libraries/Temboo/README.adoc
Normal file
32
arduino/libraries/Temboo/README.adoc
Normal file
@@ -0,0 +1,32 @@
|
||||
= Temboo Library for Arduino =
|
||||
|
||||
This library allows an Arduino to connect to the Temboo service.
|
||||
|
||||
== License ==
|
||||
|
||||
Copyright 2017, Temboo Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
either express or implied. See the License for the specific
|
||||
language governing permissions and limitations under the License.
|
||||
|
||||
This library includes elements of the Paho MQTT client, used
|
||||
with permission under the Eclipse Public License - v1.0:
|
||||
|
||||
http://www.eclipse.org/legal/epl-v10.html
|
||||
|
||||
and the Eclipse Distribution License v1.0:
|
||||
|
||||
http://www.eclipse.org/org/documents/edl-v10.php
|
||||
|
||||
The Eclipse MQTT Paho client source is available here:
|
||||
|
||||
http://www.eclipse.org/paho/
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
GetYahooWeatherReport
|
||||
|
||||
Demonstrates making a request to the Yahoo! Weather API using Temboo from an Arduino Yún.
|
||||
|
||||
Check out the latest Arduino & Temboo examples and support docs at http://www.temboo.com/arduino
|
||||
|
||||
A Temboo account and application key are necessary to run all Temboo examples.
|
||||
If you don't already have one, you can register for a free Temboo account at
|
||||
http://www.temboo.com
|
||||
|
||||
This example assumes basic familiarity with Arduino sketches, and that your Yún is connected
|
||||
to the Internet.
|
||||
|
||||
Looking for another API to use with your Arduino Yún? We've got over 100 in our Library!
|
||||
|
||||
This example code is in the public domain.
|
||||
*/
|
||||
|
||||
#include <Bridge.h>
|
||||
#include <Temboo.h>
|
||||
#include "TembooAccount.h" // contains Temboo account information
|
||||
// as described in the footer comment below
|
||||
|
||||
|
||||
// the address for which a weather forecast will be retrieved
|
||||
String ADDRESS_FOR_FORECAST = "104 Franklin St., New York NY 10013";
|
||||
|
||||
int numRuns = 1; // execution count, so that this doesn't run forever
|
||||
int maxRuns = 10; // max number of times the Yahoo WeatherByAddress Choreo should be run
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// for debugging, wait until a serial console is connected
|
||||
delay(4000);
|
||||
while(!Serial);
|
||||
Bridge.begin();
|
||||
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// while we haven't reached the max number of runs...
|
||||
if (numRuns <= maxRuns) {
|
||||
|
||||
// print status
|
||||
Serial.println("Running GetWeatherByAddress - Run #" + String(numRuns++) + "...");
|
||||
|
||||
// create a TembooChoreo object to send a Choreo request to Temboo
|
||||
TembooChoreo GetWeatherByAddressChoreo;
|
||||
|
||||
// invoke the Temboo client
|
||||
GetWeatherByAddressChoreo.begin();
|
||||
|
||||
// add your temboo account info
|
||||
GetWeatherByAddressChoreo.setAccountName(TEMBOO_ACCOUNT);
|
||||
GetWeatherByAddressChoreo.setAppKeyName(TEMBOO_APP_KEY_NAME);
|
||||
GetWeatherByAddressChoreo.setAppKey(TEMBOO_APP_KEY);
|
||||
|
||||
// set the name of the choreo we want to run
|
||||
GetWeatherByAddressChoreo.setChoreo("/Library/Yahoo/Weather/GetWeatherByAddress");
|
||||
|
||||
// set choreo inputs; in this case, the address for which to retrieve weather data
|
||||
// the Temboo client provides standardized calls to 100+ cloud APIs
|
||||
GetWeatherByAddressChoreo.addInput("Address", ADDRESS_FOR_FORECAST);
|
||||
|
||||
// add an output filter to extract the name of the city.
|
||||
GetWeatherByAddressChoreo.addOutputFilter("city", "/rss/channel/yweather:location/@city", "Response");
|
||||
|
||||
// add an output filter to extract the current temperature
|
||||
GetWeatherByAddressChoreo.addOutputFilter("temperature", "/rss/channel/item/yweather:condition/@temp", "Response");
|
||||
|
||||
// add an output filter to extract the date and time of the last report.
|
||||
GetWeatherByAddressChoreo.addOutputFilter("date", "/rss/channel/item/yweather:condition/@date", "Response");
|
||||
|
||||
// run the choreo
|
||||
GetWeatherByAddressChoreo.run();
|
||||
|
||||
// when the choreo results are available, print them to the serial monitor
|
||||
while(GetWeatherByAddressChoreo.available()) {
|
||||
|
||||
char c = GetWeatherByAddressChoreo.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
GetWeatherByAddressChoreo.close();
|
||||
|
||||
}
|
||||
|
||||
Serial.println("Waiting...");
|
||||
Serial.println("");
|
||||
delay(30000); // wait 30 seconds between GetWeatherByAddress calls
|
||||
}
|
||||
|
||||
/*
|
||||
IMPORTANT NOTE: TembooAccount.h:
|
||||
|
||||
TembooAccount.h is a file referenced by this sketch that contains your Temboo account information.
|
||||
You'll need to edit the placeholder version of TembooAccount.h included with this example sketch,
|
||||
by inserting your own Temboo account name and app key information. The contents of the file should
|
||||
look like:
|
||||
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
You can find your Temboo App Key information on the Temboo website,
|
||||
under My Account > Application Keys
|
||||
|
||||
The same TembooAccount.h file settings can be used for all Temboo SDK sketches.
|
||||
|
||||
Keeping your account information in a separate file means you can share the main .ino file without worrying
|
||||
that you forgot to delete your credentials.
|
||||
*/
|
||||
@@ -0,0 +1,4 @@
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
ReadATweet
|
||||
|
||||
Demonstrates retrieving the most recent Tweet from a user's home timeline
|
||||
using Temboo from an Arduino Yún.
|
||||
|
||||
Check out the latest Arduino & Temboo examples and support docs at http://www.temboo.com/arduino
|
||||
|
||||
A Temboo account and application key are necessary to run all Temboo examples.
|
||||
If you don't already have one, you can register for a free Temboo account at
|
||||
http://www.temboo.com
|
||||
|
||||
In order to run this sketch, you'll need to register an application using
|
||||
the Twitter dev console at https://dev.twitter.com. After creating the
|
||||
app, you'll find OAuth credentials for that application under the "OAuth Tool" tab.
|
||||
Substitute these values for the placeholders below.
|
||||
|
||||
This example assumes basic familiarity with Arduino sketches, and that your Yún
|
||||
is connected to the Internet.
|
||||
|
||||
Want to use another social API with your Arduino Yún? We've got Facebook,
|
||||
Google+, Instagram, Tumblr and more in our Library!
|
||||
|
||||
This example code is in the public domain.
|
||||
*/
|
||||
|
||||
#include <Bridge.h>
|
||||
#include <Temboo.h>
|
||||
#include "TembooAccount.h" // contains Temboo account information
|
||||
// as described in the footer comment below
|
||||
|
||||
/*** SUBSTITUTE YOUR VALUES BELOW: ***/
|
||||
|
||||
// Note that for additional security and reusability, you could
|
||||
// use #define statements to specify these values in a .h file.
|
||||
const String TWITTER_ACCESS_TOKEN = "your-twitter-access-token";
|
||||
const String TWITTER_ACCESS_TOKEN_SECRET = "your-twitter-access-token-secret";
|
||||
const String TWITTER_CONSUMER_KEY = "your-twitter-consumer-key";
|
||||
const String TWITTER_CONSUMER_SECRET = "your-twitter-consumer-secret";
|
||||
|
||||
int numRuns = 1; // execution count, so this doesn't run forever
|
||||
int maxRuns = 10; // the max number of times the Twitter HomeTimeline Choreo should run
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// For debugging, wait until a serial console is connected.
|
||||
delay(4000);
|
||||
while(!Serial);
|
||||
Bridge.begin();
|
||||
}
|
||||
void loop()
|
||||
{
|
||||
// while we haven't reached the max number of runs...
|
||||
if (numRuns <= maxRuns) {
|
||||
Serial.println("Running ReadATweet - Run #" + String(numRuns++));
|
||||
|
||||
TembooChoreo HomeTimelineChoreo;
|
||||
|
||||
// invoke the Temboo client.
|
||||
// NOTE that the client must be reinvoked, and repopulated with
|
||||
// appropriate arguments, each time its run() method is called.
|
||||
HomeTimelineChoreo.begin();
|
||||
|
||||
// set Temboo account credentials
|
||||
HomeTimelineChoreo.setAccountName(TEMBOO_ACCOUNT);
|
||||
HomeTimelineChoreo.setAppKeyName(TEMBOO_APP_KEY_NAME);
|
||||
HomeTimelineChoreo.setAppKey(TEMBOO_APP_KEY);
|
||||
|
||||
// tell the Temboo client which Choreo to run (Twitter > Timelines > HomeTimeline)
|
||||
HomeTimelineChoreo.setChoreo("/Library/Twitter/Timelines/HomeTimeline");
|
||||
|
||||
|
||||
// set the required choreo inputs
|
||||
// see https://www.temboo.com/library/Library/Twitter/Timelines/HomeTimeline/
|
||||
// for complete details about the inputs for this Choreo
|
||||
|
||||
HomeTimelineChoreo.addInput("Count", "1"); // the max number of Tweets to return from each request
|
||||
HomeTimelineChoreo.addInput("AccessToken", TWITTER_ACCESS_TOKEN);
|
||||
HomeTimelineChoreo.addInput("AccessTokenSecret", TWITTER_ACCESS_TOKEN_SECRET);
|
||||
HomeTimelineChoreo.addInput("ConsumerKey", TWITTER_CONSUMER_KEY);
|
||||
HomeTimelineChoreo.addInput("ConsumerSecret", TWITTER_CONSUMER_SECRET);
|
||||
|
||||
// next, we'll define two output filters that let us specify the
|
||||
// elements of the response from Twitter that we want to receive.
|
||||
// see the examples at http://www.temboo.com/arduino
|
||||
// for more on using output filters
|
||||
|
||||
// we want the text of the tweet
|
||||
HomeTimelineChoreo.addOutputFilter("tweet", "/[1]/text", "Response");
|
||||
|
||||
// and the name of the author
|
||||
HomeTimelineChoreo.addOutputFilter("author", "/[1]/user/screen_name", "Response");
|
||||
|
||||
|
||||
// tell the Process to run and wait for the results. The
|
||||
// return code will tell us whether the Temboo client
|
||||
// was able to send our request to the Temboo servers
|
||||
unsigned int returnCode = HomeTimelineChoreo.run();
|
||||
|
||||
// a response code of 0 means success; print the API response
|
||||
if(returnCode == 0) {
|
||||
|
||||
String author; // a String to hold the tweet author's name
|
||||
String tweet; // a String to hold the text of the tweet
|
||||
|
||||
|
||||
// choreo outputs are returned as key/value pairs, delimited with
|
||||
// newlines and record/field terminator characters, for example:
|
||||
// Name1\n\x1F
|
||||
// Value1\n\x1E
|
||||
// Name2\n\x1F
|
||||
// Value2\n\x1E
|
||||
|
||||
// see the examples at http://www.temboo.com/arduino for more details
|
||||
// we can read this format into separate variables, as follows:
|
||||
|
||||
while(HomeTimelineChoreo.available()) {
|
||||
// read the name of the output item
|
||||
String name = HomeTimelineChoreo.readStringUntil('\x1F');
|
||||
name.trim();
|
||||
|
||||
// read the value of the output item
|
||||
String data = HomeTimelineChoreo.readStringUntil('\x1E');
|
||||
data.trim();
|
||||
|
||||
// assign the value to the appropriate String
|
||||
if (name == "tweet") {
|
||||
tweet = data;
|
||||
} else if (name == "author") {
|
||||
author = data;
|
||||
}
|
||||
}
|
||||
|
||||
Serial.println("@" + author + " - " + tweet);
|
||||
|
||||
} else {
|
||||
// there was an error
|
||||
// print the raw output from the choreo
|
||||
while(HomeTimelineChoreo.available()) {
|
||||
char c = HomeTimelineChoreo.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
}
|
||||
|
||||
HomeTimelineChoreo.close();
|
||||
|
||||
}
|
||||
|
||||
Serial.println("Waiting...");
|
||||
delay(90000); // wait 90 seconds between HomeTimeline calls
|
||||
}
|
||||
|
||||
/*
|
||||
IMPORTANT NOTE: TembooAccount.h:
|
||||
|
||||
TembooAccount.h is a file referenced by this sketch that contains your Temboo account information.
|
||||
You'll need to edit the placeholder version of TembooAccount.h included with this example sketch,
|
||||
by inserting your own Temboo account name and app key information. The contents of the file should
|
||||
look like:
|
||||
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
You can find your Temboo App Key information on the Temboo website,
|
||||
under My Account > Application Keys
|
||||
|
||||
The same TembooAccount.h file settings can be used for all Temboo SDK sketches.
|
||||
|
||||
Keeping your account information in a separate file means you can share the main .ino file without worrying
|
||||
that you forgot to delete your credentials.
|
||||
*/
|
||||
@@ -0,0 +1,4 @@
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
SendATweet
|
||||
|
||||
Demonstrates sending a tweet via a Twitter account using Temboo from an Arduino Yún.
|
||||
|
||||
Check out the latest Arduino & Temboo examples and support docs at http://www.temboo.com/arduino
|
||||
|
||||
A Temboo account and application key are necessary to run all Temboo examples.
|
||||
If you don't already have one, you can register for a free Temboo account at
|
||||
http://www.temboo.com
|
||||
|
||||
In order to run this sketch, you'll need to register an application using
|
||||
the Twitter dev console at https://dev.twitter.com. Note that since this
|
||||
sketch creates a new tweet, your application will need to be configured with
|
||||
read+write permissions. After creating the app, you'll find OAuth credentials
|
||||
for that application under the "OAuth Tool" tab. Substitute these values for
|
||||
the placeholders below.
|
||||
|
||||
This example assumes basic familiarity with Arduino sketches, and that your Yún is connected
|
||||
to the Internet.
|
||||
|
||||
Want to use another social API with your Arduino Yún? We've got Facebook,
|
||||
Google+, Instagram, Tumblr and more in our Library!
|
||||
|
||||
This example code is in the public domain.
|
||||
*/
|
||||
|
||||
#include <Bridge.h>
|
||||
#include <Temboo.h>
|
||||
#include "TembooAccount.h" // contains Temboo account information
|
||||
// as described in the footer comment below
|
||||
|
||||
|
||||
/*** SUBSTITUTE YOUR VALUES BELOW: ***/
|
||||
|
||||
// Note that for additional security and reusability, you could
|
||||
// use #define statements to specify these values in a .h file.
|
||||
const String TWITTER_ACCESS_TOKEN = "your-twitter-access-token";
|
||||
const String TWITTER_ACCESS_TOKEN_SECRET = "your-twitter-access-token-secret";
|
||||
const String TWITTER_CONSUMER_KEY = "your-twitter-consumer-key";
|
||||
const String TWITTER_CONSUMER_SECRET = "your-twitter-consumer-secret";
|
||||
|
||||
int numRuns = 1; // execution count, so this sketch doesn't run forever
|
||||
int maxRuns = 3; // the max number of times the Twitter Update Choreo should run
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// for debugging, wait until a serial console is connected
|
||||
delay(4000);
|
||||
while(!Serial);
|
||||
|
||||
Bridge.begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// only try to send the tweet if we haven't already sent it successfully
|
||||
if (numRuns <= maxRuns) {
|
||||
|
||||
Serial.println("Running SendATweet - Run #" + String(numRuns++) + "...");
|
||||
|
||||
// define the text of the tweet we want to send
|
||||
String tweetText("My Arduino Yun has been running for " + String(millis()) + " milliseconds.");
|
||||
|
||||
|
||||
TembooChoreo StatusesUpdateChoreo;
|
||||
|
||||
// invoke the Temboo client
|
||||
// NOTE that the client must be reinvoked, and repopulated with
|
||||
// appropriate arguments, each time its run() method is called.
|
||||
StatusesUpdateChoreo.begin();
|
||||
|
||||
// set Temboo account credentials
|
||||
StatusesUpdateChoreo.setAccountName(TEMBOO_ACCOUNT);
|
||||
StatusesUpdateChoreo.setAppKeyName(TEMBOO_APP_KEY_NAME);
|
||||
StatusesUpdateChoreo.setAppKey(TEMBOO_APP_KEY);
|
||||
|
||||
// identify the Temboo Library choreo to run (Twitter > Tweets > StatusesUpdate)
|
||||
StatusesUpdateChoreo.setChoreo("/Library/Twitter/Tweets/StatusesUpdate");
|
||||
|
||||
// set the required choreo inputs
|
||||
// see https://www.temboo.com/library/Library/Twitter/Tweets/StatusesUpdate/
|
||||
// for complete details about the inputs for this Choreo
|
||||
|
||||
// add the Twitter account information
|
||||
StatusesUpdateChoreo.addInput("AccessToken", TWITTER_ACCESS_TOKEN);
|
||||
StatusesUpdateChoreo.addInput("AccessTokenSecret", TWITTER_ACCESS_TOKEN_SECRET);
|
||||
StatusesUpdateChoreo.addInput("ConsumerKey", TWITTER_CONSUMER_KEY);
|
||||
StatusesUpdateChoreo.addInput("ConsumerSecret", TWITTER_CONSUMER_SECRET);
|
||||
|
||||
// and the tweet we want to send
|
||||
StatusesUpdateChoreo.addInput("StatusUpdate", tweetText);
|
||||
|
||||
// tell the Process to run and wait for the results. The
|
||||
// return code (returnCode) will tell us whether the Temboo client
|
||||
// was able to send our request to the Temboo servers
|
||||
unsigned int returnCode = StatusesUpdateChoreo.run();
|
||||
|
||||
// a return code of zero (0) means everything worked
|
||||
if (returnCode == 0) {
|
||||
Serial.println("Success! Tweet sent!");
|
||||
} else {
|
||||
// a non-zero return code means there was an error
|
||||
// read and print the error message
|
||||
while (StatusesUpdateChoreo.available()) {
|
||||
char c = StatusesUpdateChoreo.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
}
|
||||
StatusesUpdateChoreo.close();
|
||||
|
||||
// do nothing for the next 90 seconds
|
||||
Serial.println("Waiting...");
|
||||
delay(90000);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
IMPORTANT NOTE: TembooAccount.h:
|
||||
|
||||
TembooAccount.h is a file referenced by this sketch that contains your Temboo account information.
|
||||
You'll need to edit the placeholder version of TembooAccount.h included with this example sketch,
|
||||
by inserting your own Temboo account name and app key information. The contents of the file should
|
||||
look like:
|
||||
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
You can find your Temboo App Key information on the Temboo website,
|
||||
under My Account > Application Keys
|
||||
|
||||
The same TembooAccount.h file settings can be used for all Temboo SDK sketches.
|
||||
|
||||
Keeping your account information in a separate file means you can share the main .ino file without worrying
|
||||
that you forgot to delete your credentials.
|
||||
*/
|
||||
@@ -0,0 +1,4 @@
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
SendAnEmail
|
||||
|
||||
Demonstrates sending an email via a Google Gmail account using Temboo from an Arduino Yún.
|
||||
|
||||
Check out the latest Arduino & Temboo examples and tutorials at http://www.temboo.com/arduino
|
||||
|
||||
A Temboo account and application key are necessary to run all Temboo examples.
|
||||
If you don't already have one, you can register for a free Temboo account at
|
||||
http://www.temboo.com
|
||||
|
||||
Instructions:
|
||||
|
||||
1. Create a Temboo account: http://www.temboo.com
|
||||
|
||||
2. Retrieve your Temboo application details: http://www.temboo.com/account/applications
|
||||
|
||||
3. Replace the values in the TembooAccount.h tab with your Temboo application details
|
||||
|
||||
4. You'll also need a Gmail account. Update the placeholder Gmail address in the code
|
||||
below with your own details.
|
||||
|
||||
https://www.gmail.com
|
||||
|
||||
5. Once you have a Gmail account, turn on 2-step authentication, and create an application-specific
|
||||
password to allow Temboo to access your Google account: https://www.google.com/landing/2step/.
|
||||
|
||||
6. After you've enabled 2-Step authentication, you'll need to create an App Password:
|
||||
https://security.google.com/settings/security/apppasswords
|
||||
|
||||
7. In the "Select app" dropdown menu, choose "Other", and give your app a name (e.g., TembooApp).
|
||||
|
||||
8. Click "Generate". You'll be given a 16-digit passcode that can be used to access your Google Account from Temboo.
|
||||
|
||||
9. Copy and paste this password into the code below, updating the GMAIL_APP_PASSWORD variable
|
||||
|
||||
10. Upload the sketch to your Arduino Yún and open the serial monitor
|
||||
|
||||
NOTE: You can test this Choreo and find the latest instructions on our website:
|
||||
https://temboo.com/library/Library/Google/Gmail/SendEmail
|
||||
|
||||
You can also find an in-depth version of this example here:
|
||||
https://temboo.com/arduino/yun/send-an-email
|
||||
|
||||
This example assumes basic familiarity with Arduino sketches, and that your Yún is connected
|
||||
to the Internet.
|
||||
|
||||
Looking for another API to use with your Arduino Yún? We've got over 100 in our Library!
|
||||
|
||||
This example code is in the public domain.
|
||||
*/
|
||||
|
||||
#include <Bridge.h>
|
||||
#include <Temboo.h>
|
||||
#include "TembooAccount.h" // contains Temboo account information
|
||||
// as described in the footer comment below
|
||||
|
||||
/*** SUBSTITUTE YOUR VALUES BELOW: ***/
|
||||
|
||||
// Note that for additional security and reusability, you could
|
||||
// use #define statements to specify these values in a .h file.
|
||||
|
||||
// your Gmail username, formatted as a complete email address, eg "bob.smith@gmail.com"
|
||||
const String GMAIL_USER_NAME = "xxxxxxxxxx";
|
||||
|
||||
// your application specific password (see instructions above)
|
||||
const String GMAIL_APP_PASSWORD = "xxxxxxxxxx";
|
||||
|
||||
// the email address you want to send the email to, eg "jane.doe@temboo.com"
|
||||
const String TO_EMAIL_ADDRESS = "xxxxxxxxxx";
|
||||
|
||||
// a flag to indicate whether we've tried to send the email yet or not
|
||||
boolean attempted = false;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// for debugging, wait until a serial console is connected
|
||||
delay(4000);
|
||||
while(!Serial);
|
||||
|
||||
Bridge.begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// only try to send the email if we haven't already tried
|
||||
if (!attempted) {
|
||||
|
||||
Serial.println("Running SendAnEmail...");
|
||||
|
||||
TembooChoreo SendEmailChoreo;
|
||||
|
||||
// invoke the Temboo client
|
||||
// NOTE that the client must be reinvoked, and repopulated with
|
||||
// appropriate arguments, each time its run() method is called.
|
||||
SendEmailChoreo.begin();
|
||||
|
||||
// set Temboo account credentials
|
||||
SendEmailChoreo.setAccountName(TEMBOO_ACCOUNT);
|
||||
SendEmailChoreo.setAppKeyName(TEMBOO_APP_KEY_NAME);
|
||||
SendEmailChoreo.setAppKey(TEMBOO_APP_KEY);
|
||||
|
||||
// identify the Temboo Library choreo to run (Google > Gmail > SendEmail)
|
||||
SendEmailChoreo.setChoreo("/Library/Google/Gmail/SendEmail");
|
||||
|
||||
|
||||
// set the required choreo inputs
|
||||
// see https://www.temboo.com/library/Library/Google/Gmail/SendEmail/
|
||||
// for complete details about the inputs for this Choreo
|
||||
|
||||
// the first input is your Gmail email address.
|
||||
SendEmailChoreo.addInput("Username", GMAIL_USER_NAME);
|
||||
// next is your application specific password
|
||||
SendEmailChoreo.addInput("Password", GMAIL_APP_PASSWORD);
|
||||
// who to send the email to
|
||||
SendEmailChoreo.addInput("ToAddress", TO_EMAIL_ADDRESS);
|
||||
// then a subject line
|
||||
SendEmailChoreo.addInput("Subject", "ALERT: Greenhouse Temperature");
|
||||
|
||||
// next comes the message body, the main content of the email
|
||||
SendEmailChoreo.addInput("MessageBody", "Hey! The greenhouse is too cold!");
|
||||
|
||||
// tell the Choreo to run and wait for the results. The
|
||||
// return code (returnCode) will tell us whether the Temboo client
|
||||
// was able to send our request to the Temboo servers
|
||||
unsigned int returnCode = SendEmailChoreo.run();
|
||||
|
||||
// a return code of zero (0) means everything worked
|
||||
if (returnCode == 0) {
|
||||
Serial.println("Success! Email sent!");
|
||||
} else {
|
||||
// a non-zero return code means there was an error
|
||||
// read and print the error message
|
||||
while (SendEmailChoreo.available()) {
|
||||
char c = SendEmailChoreo.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
}
|
||||
SendEmailChoreo.close();
|
||||
|
||||
// set the flag showing we've tried
|
||||
attempted = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
IMPORTANT NOTE: TembooAccount.h:
|
||||
|
||||
TembooAccount.h is a file referenced by this sketch that contains your Temboo account information.
|
||||
You'll need to edit the placeholder version of TembooAccount.h included with this example sketch,
|
||||
by inserting your own Temboo account name and app key information. The contents of the file should
|
||||
look like:
|
||||
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
You can find your Temboo App Key information on the Temboo website,
|
||||
under My Account > Application Keys
|
||||
|
||||
The same TembooAccount.h file settings can be used for all Temboo SDK sketches.
|
||||
|
||||
Keeping your account information in a separate file means you can share the main .ino file without worrying
|
||||
that you forgot to delete your credentials.
|
||||
*/
|
||||
@@ -0,0 +1,4 @@
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
SendAnSMS
|
||||
|
||||
Demonstrates sending an SMS via Twilio using Temboo from an Arduino Yún.
|
||||
|
||||
Check out the latest Arduino & Temboo examples and support docs at http://www.temboo.com/arduino
|
||||
|
||||
A Temboo account and application key are necessary to run all Temboo examples.
|
||||
If you don't already have one, you can register for a free Temboo account at
|
||||
http://www.temboo.com
|
||||
|
||||
Since this sketch uses Twilio to send the SMS, you'll also need a valid
|
||||
Twilio account. You can create one for free at https://www.twilio.com.
|
||||
|
||||
The sketch needs your Twilio phone number, along with
|
||||
the Account SID and Auth Token you get when you register with Twilio.
|
||||
Make sure to use the Account SID and Auth Token from your Twilio Dashboard
|
||||
(not your test credentials from the Dev Tools panel).
|
||||
|
||||
Also note that if you're using a free Twilio account, you'll need to verify
|
||||
the phone number to which messages are being sent by going to twilio.com and following
|
||||
the instructions under the "Numbers > Verified Caller IDs" tab (this restriction
|
||||
doesn't apply if you have a paid Twilio account).
|
||||
|
||||
This example assumes basic familiarity with Arduino sketches, and that your Yún is connected
|
||||
to the Internet.
|
||||
|
||||
Looking for another API to use with your Arduino Yún? We've got over 100 in our Library!
|
||||
|
||||
This example code is in the public domain.
|
||||
*/
|
||||
|
||||
#include <Bridge.h>
|
||||
#include <Temboo.h>
|
||||
#include "TembooAccount.h" // contains Temboo account information
|
||||
// as described in the footer comment below
|
||||
|
||||
|
||||
|
||||
/*** SUBSTITUTE YOUR VALUES BELOW: ***/
|
||||
|
||||
// Note that for additional security and reusability, you could
|
||||
// use #define statements to specify these values in a .h file.
|
||||
|
||||
// the Account SID from your Twilio account
|
||||
const String TWILIO_ACCOUNT_SID = "xxxxxxxxxx";
|
||||
|
||||
// the Auth Token from your Twilio account
|
||||
const String TWILIO_AUTH_TOKEN = "xxxxxxxxxx";
|
||||
|
||||
// your Twilio phone number, e.g., "+1 555-222-1212"
|
||||
const String TWILIO_NUMBER = "xxxxxxxxxx";
|
||||
|
||||
// the number to which the SMS should be sent, e.g., "+1 555-222-1212"
|
||||
const String RECIPIENT_NUMBER = "xxxxxxxxxx";
|
||||
|
||||
// a flag to indicate whether we've attempted to send the SMS yet or not
|
||||
boolean attempted = false;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// for debugging, wait until a serial console is connected
|
||||
delay(4000);
|
||||
while(!Serial);
|
||||
|
||||
Bridge.begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// only try to send the SMS if we haven't already sent it successfully
|
||||
if (!attempted) {
|
||||
|
||||
Serial.println("Running SendAnSMS...");
|
||||
|
||||
// we need a Process object to send a Choreo request to Temboo
|
||||
TembooChoreo SendSMSChoreo;
|
||||
|
||||
// invoke the Temboo client
|
||||
// NOTE that the client must be reinvoked and repopulated with
|
||||
// appropriate arguments each time its run() method is called.
|
||||
SendSMSChoreo.begin();
|
||||
|
||||
// set Temboo account credentials
|
||||
SendSMSChoreo.setAccountName(TEMBOO_ACCOUNT);
|
||||
SendSMSChoreo.setAppKeyName(TEMBOO_APP_KEY_NAME);
|
||||
SendSMSChoreo.setAppKey(TEMBOO_APP_KEY);
|
||||
|
||||
// identify the Temboo Library choreo to run (Twilio > SMSMessages > SendSMS)
|
||||
SendSMSChoreo.setChoreo("/Library/Twilio/SMSMessages/SendSMS");
|
||||
|
||||
// set the required choreo inputs
|
||||
// see https://www.temboo.com/library/Library/Twilio/SMSMessages/SendSMS/
|
||||
// for complete details about the inputs for this Choreo
|
||||
|
||||
// the first input is a your AccountSID
|
||||
SendSMSChoreo.addInput("AccountSID", TWILIO_ACCOUNT_SID);
|
||||
|
||||
// next is your Auth Token
|
||||
SendSMSChoreo.addInput("AuthToken", TWILIO_AUTH_TOKEN);
|
||||
|
||||
// next is your Twilio phone number
|
||||
SendSMSChoreo.addInput("From", TWILIO_NUMBER);
|
||||
|
||||
// next, what number to send the SMS to
|
||||
SendSMSChoreo.addInput("To", RECIPIENT_NUMBER);
|
||||
|
||||
// finally, the text of the message to send
|
||||
SendSMSChoreo.addInput("Body", "Hey, there! This is a message from your Arduino Yun!");
|
||||
|
||||
// tell the Process to run and wait for the results. The
|
||||
// return code (returnCode) will tell us whether the Temboo client
|
||||
// was able to send our request to the Temboo servers
|
||||
unsigned int returnCode = SendSMSChoreo.run();
|
||||
|
||||
// a return code of zero (0) means everything worked
|
||||
if (returnCode == 0) {
|
||||
Serial.println("Success! SMS sent!");
|
||||
} else {
|
||||
// a non-zero return code means there was an error
|
||||
// read and print the error message
|
||||
while (SendSMSChoreo.available()) {
|
||||
char c = SendSMSChoreo.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
}
|
||||
SendSMSChoreo.close();
|
||||
|
||||
// set the flag indicatine we've tried once.
|
||||
attempted=true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
IMPORTANT NOTE: TembooAccount.h:
|
||||
|
||||
TembooAccount.h is a file referenced by this sketch that contains your Temboo account information.
|
||||
You'll need to edit the placeholder version of TembooAccount.h included with this example sketch,
|
||||
by inserting your own Temboo account name and app key information. The contents of the file should
|
||||
look like:
|
||||
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
You can find your Temboo App Key information on the Temboo website,
|
||||
under My Account > Application Keys
|
||||
|
||||
The same TembooAccount.h file settings can be used for all Temboo SDK sketches.
|
||||
|
||||
Keeping your account information in a separate file means you can share the main .ino file without worrying
|
||||
that you forgot to delete your credentials.
|
||||
*/
|
||||
@@ -0,0 +1,5 @@
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
SendDataToGoogleSpreadsheet
|
||||
|
||||
Demonstrates appending a row of data to a Google spreadsheet using Temboo from an Arduino Yún.
|
||||
|
||||
Check out the latest Arduino & Temboo examples and tutorials at http://www.temboo.com/arduino
|
||||
|
||||
A Temboo account and application key are necessary to run all Temboo examples.
|
||||
If you don't already have one, you can register for a free Temboo account at
|
||||
http://www.temboo.com
|
||||
|
||||
Instructions:
|
||||
|
||||
1. Create a Temboo account: http://www.temboo.com
|
||||
|
||||
2. Retrieve your Temboo application details: http://www.temboo.com/account/applications
|
||||
|
||||
3. Replace the values in the TembooAccount.h tab with your Temboo application details
|
||||
|
||||
4. You'll also need a Google Spreadsheet that includes a title in the first row
|
||||
of each column that data will be written to. This example assumes there are two columns.
|
||||
The first column is the time (in milliseconds) that the row was appended, and the second
|
||||
column is a sensor value. In other words, your spreadsheet should look like:
|
||||
|
||||
Time | Sensor Value |
|
||||
------+-----------------
|
||||
| |
|
||||
|
||||
5. Google Spreadsheets requires you to authenticate via OAuth. Follow the steps
|
||||
in the link below to find your ClientID, ClientSecret, and RefreshToken, and then
|
||||
use those values to overwrite the placeholders in the code below.
|
||||
|
||||
https://temboo.com/library/Library/Google/OAuth/
|
||||
|
||||
For the scope field, you need to use: https://www.googleapis.com/auth/spreadsheets
|
||||
|
||||
Here's a video outlines how Temboo helps with the OAuth process:
|
||||
|
||||
https://www.temboo.com/videos#oauthchoreos
|
||||
|
||||
And here's a more in-depth version of this example on our website:
|
||||
|
||||
https://temboo.com/arduino/yun/update-google-spreadsheet
|
||||
|
||||
6. Next, upload the sketch to your Arduino Yún and open the serial monitor
|
||||
|
||||
Note: you can test this Choreo and find the latest instructions on our website:
|
||||
https://temboo.com/library/Library/Google/Sheets/AppendValues/
|
||||
|
||||
Looking for another API to use with your Arduino Yún? We've got over 100 in our Library!
|
||||
|
||||
This example code is in the public domain.
|
||||
|
||||
*/
|
||||
|
||||
#include <Bridge.h>
|
||||
#include <Temboo.h>
|
||||
#include "TembooAccount.h" // contains Temboo account information,
|
||||
// as described in the footer comment below
|
||||
|
||||
|
||||
/*** SUBSTITUTE YOUR VALUES BELOW: ***/
|
||||
|
||||
// Note that for additional security and reusability, you could
|
||||
// use #define statements to specify these values in a .h file.
|
||||
|
||||
// the clientID found in Google's Developer Console under API Manager > Credentials
|
||||
const String CLIENT_ID = "your-client-id";
|
||||
|
||||
// the clientSecret found in Google's Developer Console under API Manager > Credentials
|
||||
const String CLIENT_SECRET = "your-client-secret";
|
||||
|
||||
// returned after running FinalizeOAuth
|
||||
const String REFRESH_TOKEN = "your-oauth-refresh-token";
|
||||
|
||||
// The ID of the spreadsheet you want to send data to
|
||||
// which can be found in the URL when viewing your spreadsheet at Google. For example,
|
||||
// the ID in the URL below is: "1tvFW2n-xFFJCE1q5j0HTetOsDhhgw7_998_K4sFtk"
|
||||
// Sample URL: https://docs.google.com/spreadsheets/d/1tvFW2n-xFFJCE1q5j0HTetOsDhhgw7_998_K4sFtk/edit
|
||||
const String SPREADSHEET_ID = "your-spreadsheet-id";
|
||||
|
||||
const unsigned long RUN_INTERVAL_MILLIS = 60000; // how often to run the Choreo (in milliseconds)
|
||||
|
||||
// the last time we ran the Choreo
|
||||
// (initialized to 60 seconds ago so the
|
||||
// Choreo is run immediately when we start up)
|
||||
unsigned long lastRun = (unsigned long)-60000;
|
||||
|
||||
void setup() {
|
||||
|
||||
// for debugging, wait until a serial console is connected
|
||||
Serial.begin(9600);
|
||||
delay(4000);
|
||||
while(!Serial);
|
||||
|
||||
Serial.print("Initializing the bridge...");
|
||||
Bridge.begin();
|
||||
Serial.println("Done");
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// get the number of milliseconds this sketch has been running
|
||||
unsigned long now = millis();
|
||||
|
||||
// run again if it's been 60 seconds since we last ran
|
||||
if (now - lastRun >= RUN_INTERVAL_MILLIS) {
|
||||
|
||||
// remember 'now' as the last time we ran the choreo
|
||||
lastRun = now;
|
||||
|
||||
Serial.println("Getting sensor value...");
|
||||
|
||||
// get the value we want to append to our spreadsheet
|
||||
unsigned long sensorValue = getSensorValue();
|
||||
|
||||
Serial.println("Appending value to spreadsheet...");
|
||||
|
||||
// we need a Process object to send a Choreo request to Temboo
|
||||
TembooChoreo AppendValuesChoreo;
|
||||
|
||||
// invoke the Temboo client
|
||||
// NOTE that the client must be reinvoked and repopulated with
|
||||
// appropriate arguments each time its run() method is called.
|
||||
AppendValuesChoreo.begin();
|
||||
|
||||
// set Temboo account credentials
|
||||
AppendValuesChoreo.setAccountName(TEMBOO_ACCOUNT);
|
||||
AppendValuesChoreo.setAppKeyName(TEMBOO_APP_KEY_NAME);
|
||||
AppendValuesChoreo.setAppKey(TEMBOO_APP_KEY);
|
||||
|
||||
// identify the Temboo Library choreo to run (Google > Sheets > AppendValues)
|
||||
AppendValuesChoreo.setChoreo("/Library/Google/Sheets/AppendValues");
|
||||
|
||||
// set the required Choreo inputs
|
||||
// see https://www.temboo.com/library/Library/Google/Sheets/AppendValues/
|
||||
// for complete details about the inputs for this Choreo
|
||||
|
||||
// your Google application client ID
|
||||
AppendValuesChoreo.addInput("ClientID", CLIENT_ID);
|
||||
// your Google application client secret
|
||||
AppendValuesChoreo.addInput("ClientSecret", CLIENT_SECRET);
|
||||
// your Google OAuth refresh token
|
||||
AppendValuesChoreo.addInput("RefreshToken", REFRESH_TOKEN);
|
||||
|
||||
// the ID of the spreadsheet you want to append to
|
||||
AppendValuesChoreo.addInput("SpreadsheetID", SPREADSHEET_ID);
|
||||
|
||||
// convert the time and sensor values to a comma separated string
|
||||
String rowData = "[[\"" + String(now) + "\", \"" + String(sensorValue) + "\"]]";
|
||||
|
||||
// add the RowData input item
|
||||
AppendValuesChoreo.addInput("Values", rowData);
|
||||
|
||||
// run the Choreo and wait for the results
|
||||
// The return code (returnCode) will indicate success or failure
|
||||
unsigned int returnCode = AppendValuesChoreo.run();
|
||||
|
||||
// return code of zero (0) means success
|
||||
if (returnCode == 0) {
|
||||
Serial.println("Success! Appended " + rowData);
|
||||
Serial.println("");
|
||||
} else {
|
||||
// return code of anything other than zero means failure
|
||||
// read and display any error messages
|
||||
while (AppendValuesChoreo.available()) {
|
||||
char c = AppendValuesChoreo.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
}
|
||||
|
||||
AppendValuesChoreo.close();
|
||||
}
|
||||
}
|
||||
|
||||
// this function simulates reading the value of a sensor
|
||||
unsigned long getSensorValue() {
|
||||
return analogRead(A0);
|
||||
}
|
||||
|
||||
/*
|
||||
IMPORTANT NOTE: TembooAccount.h:
|
||||
|
||||
TembooAccount.h is a file referenced by this sketch that contains your Temboo account information.
|
||||
You'll need to edit the placeholder version of TembooAccount.h included with this example sketch,
|
||||
by inserting your own Temboo account name and app key information. The contents of the file should
|
||||
look like:
|
||||
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
You can find your Temboo App Key information on the Temboo website,
|
||||
under My Account > Application Keys
|
||||
|
||||
The same TembooAccount.h file settings can be used for all Temboo SDK sketches.
|
||||
|
||||
Keeping your account information in a separate file means you can share the main .ino file without worrying
|
||||
that you forgot to delete your credentials.
|
||||
*/
|
||||
@@ -0,0 +1,5 @@
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright 2017, Temboo Inc.
|
||||
|
||||
#include <Process.h>
|
||||
|
||||
void setup() {
|
||||
// initialize the Bridge
|
||||
Bridge.begin();
|
||||
Serial.begin(9600);
|
||||
Process p;
|
||||
|
||||
//intro message
|
||||
Serial.println("**** Temboo Cloud Controls ****\n");
|
||||
|
||||
// update the package list
|
||||
Serial.print("Updating package listings...");
|
||||
p.runShellCommand("opkg update");
|
||||
int returnCode = p.exitValue();
|
||||
if (returnCode == 0) {
|
||||
Serial.println("Success!");
|
||||
} else {
|
||||
Serial.println("Failed. Make sure your device is connected to the internet properly.");
|
||||
while(p.available()) {
|
||||
char c = p.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Serial.println();
|
||||
// upgrade the Temboo package
|
||||
Serial.print("Updating Temboo...");
|
||||
p.runShellCommand("opkg install http://downloads.arduino.cc/openwrtyun/1/packages/temboo_1.4.0-1_ar71xx.ipk");
|
||||
returnCode = p.exitValue();
|
||||
if (returnCode == 0) {
|
||||
Serial.println("Success!");
|
||||
} else {
|
||||
Serial.println("Failed.");
|
||||
while(p.available()) {
|
||||
char c = p.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// install python openssl to allow for for ssl connections
|
||||
Serial.print("Installing python-openssl...");
|
||||
p.runShellCommand("opkg install python-openssl");
|
||||
returnCode = p.exitValue();
|
||||
if (returnCode == 0) {
|
||||
Serial.println("Success!");
|
||||
} else {
|
||||
Serial.println("Failed.");
|
||||
while(p.available()) {
|
||||
char c = p.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// Installing twisted web to work with CoAP gateway
|
||||
Serial.print("Installing twisted-web...");
|
||||
p.runShellCommand("opkg install twisted-web");
|
||||
returnCode = p.exitValue();
|
||||
if (returnCode == 0) {
|
||||
Serial.println("Success!");
|
||||
} else {
|
||||
Serial.println("Failed.");
|
||||
while(p.available()) {
|
||||
char c = p.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// Configuring zope
|
||||
Serial.print("Configuring zope...");
|
||||
p.runShellCommand("touch /usr/lib/python2.7/site-packages/zope/__init__.py");
|
||||
returnCode = p.exitValue();
|
||||
if (returnCode == 0) {
|
||||
Serial.println("Success!");
|
||||
} else {
|
||||
Serial.println("Failed.");
|
||||
while(p.available()) {
|
||||
char c = p.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Serial.println();
|
||||
Serial.println("Update Complete - your Yun is ready for Cloud Controls!");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// do nothing
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
ToxicFacilitiesSearch
|
||||
|
||||
Demonstrates making a request to the Envirofacts API using Temboo from an Arduino Yún.
|
||||
This example retrieves the names and addresses of EPA-regulated facilities in the
|
||||
Toxins Release Inventory (TRI) database within a given zip code.
|
||||
|
||||
Check out the latest Arduino & Temboo examples and support docs at http://www.temboo.com/arduino
|
||||
|
||||
A Temboo account and application key are necessary to run all Temboo examples.
|
||||
If you don't already have one, you can register for a free Temboo account at
|
||||
http://www.temboo.com
|
||||
|
||||
This example assumes basic familiarity with Arduino sketches, and that your Yún is connected
|
||||
to the Internet.
|
||||
|
||||
Looking for another API to use with your Arduino Yún? We've got over 100 in our Library!
|
||||
|
||||
This example code is in the public domain.
|
||||
*/
|
||||
|
||||
#include <Bridge.h>
|
||||
#include <Temboo.h>
|
||||
#include "TembooAccount.h" // contains Temboo account information
|
||||
// as described in the footer comment below
|
||||
|
||||
// the zip code to search for toxin-emitting facilities
|
||||
String US_ZIP_CODE = "11215";
|
||||
|
||||
int numRuns = 1; // execution count, so that this doesn't run forever
|
||||
int maxRuns = 10; // max number of times the Envirofacts FacilitiesSearch Choreo should be run
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// for debugging, wait until a serial console is connected
|
||||
delay(4000);
|
||||
while(!Serial);
|
||||
Bridge.begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// while we haven't reached the max number of runs...
|
||||
if (numRuns <= maxRuns) {
|
||||
|
||||
// print status
|
||||
Serial.println("Running ToxicFacilitiesSearch - Run #" + String(numRuns++) + "...");
|
||||
|
||||
// we need a Process object to send a Choreo request to Temboo
|
||||
TembooChoreo FacilitiesSearchByZipChoreo;
|
||||
|
||||
// invoke the Temboo client
|
||||
// NOTE that the client must be reinvoked and repopulated with
|
||||
// appropriate arguments each time its run() method is called.
|
||||
FacilitiesSearchByZipChoreo.begin();
|
||||
|
||||
// set Temboo account credentials
|
||||
FacilitiesSearchByZipChoreo.setAccountName(TEMBOO_ACCOUNT);
|
||||
FacilitiesSearchByZipChoreo.setAppKeyName(TEMBOO_APP_KEY_NAME);
|
||||
FacilitiesSearchByZipChoreo.setAppKey(TEMBOO_APP_KEY);
|
||||
|
||||
// identify the Temboo Library choreo to run (EnviroFacts > Toxins > FacilitiesSearchByZip)
|
||||
FacilitiesSearchByZipChoreo.setChoreo("/Library/EnviroFacts/Toxins/FacilitiesSearchByZip");
|
||||
|
||||
// set choreo inputs; in this case, the US zip code for which to retrieve toxin release data
|
||||
// the Temboo client provides standardized calls to 100+ cloud APIs
|
||||
FacilitiesSearchByZipChoreo.addInput("Zip", US_ZIP_CODE);
|
||||
|
||||
// specify two output filters, to help simplify the Envirofacts API results.
|
||||
// see the tutorials on using Temboo SDK output filters at http://www.temboo.com/arduino
|
||||
FacilitiesSearchByZipChoreo.addOutputFilter("fac", "FACILITY_NAME", "Response");
|
||||
|
||||
FacilitiesSearchByZipChoreo.addOutputFilter("addr", "STREET_ADDRESS", "Response");
|
||||
|
||||
// run the choreo
|
||||
unsigned int returnCode = FacilitiesSearchByZipChoreo.run();
|
||||
if (returnCode == 0) {
|
||||
String facilities;
|
||||
String addresses;
|
||||
|
||||
// when the choreo results are available, process them.
|
||||
// the output filters we specified will return comma delimited
|
||||
// lists containing the name and street address of the facilities
|
||||
// located in the specified zip code.
|
||||
while(FacilitiesSearchByZipChoreo.available()) {
|
||||
String name = FacilitiesSearchByZipChoreo.readStringUntil('\x1F');
|
||||
name.trim();
|
||||
|
||||
String data = FacilitiesSearchByZipChoreo.readStringUntil('\x1E');
|
||||
data.trim();
|
||||
|
||||
if (name == "fac") {
|
||||
facilities = data;
|
||||
} else if (name == "addr") {
|
||||
addresses = data;
|
||||
}
|
||||
}
|
||||
FacilitiesSearchByZipChoreo.close();
|
||||
|
||||
// parse the comma delimited lists of facilities to join the
|
||||
// name with the address and print it to the serial monitor
|
||||
if (facilities.length() > 0) {
|
||||
int i = -1;
|
||||
int facilityStart = 0;
|
||||
int addressStart = 0;
|
||||
String facility;
|
||||
String address;
|
||||
do {
|
||||
i = facilities.indexOf(',', facilityStart);
|
||||
if (i >= 0) {
|
||||
facility = facilities.substring(facilityStart, i);
|
||||
facilityStart = i + 1;
|
||||
}
|
||||
|
||||
i = addresses.indexOf(',', addressStart);
|
||||
if (i >= 0) {
|
||||
address = addresses.substring(addressStart, i);
|
||||
addressStart = i + 1;
|
||||
}
|
||||
|
||||
if (i >= 0) {
|
||||
printResult(facility, address);
|
||||
}
|
||||
|
||||
}while (i >= 0);
|
||||
facility = facilities.substring(facilityStart);
|
||||
address = addresses.substring(addressStart);
|
||||
printResult(facility, address);
|
||||
} else {
|
||||
Serial.println("No facilities found in zip code " + US_ZIP_CODE);
|
||||
}
|
||||
} else {
|
||||
while(FacilitiesSearchByZipChoreo.available()) {
|
||||
char c = FacilitiesSearchByZipChoreo.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
Serial.println("Waiting...");
|
||||
Serial.println("");
|
||||
delay(30000); // wait 30 seconds between calls
|
||||
}
|
||||
|
||||
// a simple utility function, to output the facility name and address in the serial monitor.
|
||||
void printResult(String facility, String address) {
|
||||
Serial.print(facility);
|
||||
Serial.print(" - ");
|
||||
Serial.println(address);
|
||||
}
|
||||
|
||||
/*
|
||||
IMPORTANT NOTE: TembooAccount.h:
|
||||
|
||||
TembooAccount.h is a file referenced by this sketch that contains your Temboo account information.
|
||||
You'll need to edit the placeholder version of TembooAccount.h included with this example sketch,
|
||||
by inserting your own Temboo account name and app key information. The contents of the file should
|
||||
look like:
|
||||
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
You can find your Temboo App Key information on the Temboo website,
|
||||
under My Account > Application Keys
|
||||
|
||||
The same TembooAccount.h file settings can be used for all Temboo SDK sketches.
|
||||
|
||||
Keeping your account information in a separate file means you can share the main .ino file without worrying
|
||||
that you forgot to delete your credentials.
|
||||
*/
|
||||
@@ -0,0 +1,5 @@
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
UpdateFacebookStatus
|
||||
|
||||
Demonstrates sending a Facebook status update using Temboo from an Arduino Yún.
|
||||
|
||||
Check out the latest Arduino & Temboo examples and support docs at http://www.temboo.com/arduino
|
||||
|
||||
A Temboo account and application key are necessary to run all Temboo examples.
|
||||
If you don't already have one, you can register for a free Temboo account at
|
||||
http://www.temboo.com
|
||||
|
||||
In order to run this sketch, you'll need to register an application using
|
||||
the Facebook dev console at https://developers.facebook.com/apps -- after creating
|
||||
the app, log in to Temboo and visit https://www.temboo.com/library/Library/Facebook/Publishing/SetStatus/
|
||||
to use our OAuth Wizard (or OAuth Choreos) to obtain a Facebook access token.
|
||||
Substitute your access token for the placeholder value of FACEBOOK_ACCESS_TOKEN below.
|
||||
|
||||
This example assumes basic familiarity with Arduino sketches, and that your Yún
|
||||
is connected to the Internet.
|
||||
|
||||
Want to use another social API with your Arduino Yún? We've got Twitter, Google+,
|
||||
Instagram, Tumblr and more in our Library!
|
||||
|
||||
This example code is in the public domain.
|
||||
*/
|
||||
|
||||
#include <Bridge.h>
|
||||
#include <Temboo.h>
|
||||
#include "TembooAccount.h" // contains Temboo account information,
|
||||
// as described in the footer comment below
|
||||
|
||||
/*** SUBSTITUTE YOUR VALUES BELOW: ***/
|
||||
|
||||
// Note that for additional security and reusability, you could
|
||||
// use a #define statement to specify this value in a .h file.
|
||||
|
||||
// the Facebook Access Token, which can be obtained using the Temboo OAuth Wizard or Choreos
|
||||
const String FACEBOOK_ACCESS_TOKEN = "xxxxxxxxxx";
|
||||
|
||||
|
||||
int numRuns = 1; // execution count, so this sketch doesn't run forever
|
||||
int maxRuns = 10; // the max number of times the Facebook SetStatus Choreo should run
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// For debugging, wait until a serial console is connected.
|
||||
delay(4000);
|
||||
while(!Serial);
|
||||
Bridge.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// while we haven't reached the max number of runs...
|
||||
if (numRuns <= maxRuns) {
|
||||
|
||||
// print status
|
||||
Serial.println("Running UpdateFacebookStatus - Run #" + String(numRuns++) + "...");
|
||||
|
||||
// Define the status message we want to post on Facebook; since Facebook
|
||||
// doesn't allow duplicate status messages, we'll include a changing value.
|
||||
String statusMsg = "My Arduino Yun has been running for " + String(millis()) + " milliseconds!";
|
||||
|
||||
// define the Process that will be used to call the "temboo" client
|
||||
TembooChoreo SetStatusChoreo;
|
||||
|
||||
// invoke the Temboo client
|
||||
// NOTE that the client must be reinvoked and repopulated with
|
||||
// appropriate arguments each time its run() method is called.
|
||||
SetStatusChoreo.begin();
|
||||
|
||||
// set Temboo account credentials
|
||||
SetStatusChoreo.setAccountName(TEMBOO_ACCOUNT);
|
||||
SetStatusChoreo.setAppKeyName(TEMBOO_APP_KEY_NAME);
|
||||
SetStatusChoreo.setAppKey(TEMBOO_APP_KEY);
|
||||
|
||||
// tell the Temboo client which Choreo to run (Facebook > Publishing > SetStatus)
|
||||
SetStatusChoreo.setChoreo("/Library/Facebook/Publishing/SetStatus");
|
||||
|
||||
// set the required choreo inputs
|
||||
// see https://www.temboo.com/library/Library/Facebook/Publishing/SetStatus/
|
||||
// for complete details about the inputs for this Choreo
|
||||
|
||||
SetStatusChoreo.addInput("AccessToken", FACEBOOK_ACCESS_TOKEN);
|
||||
SetStatusChoreo.addInput("Message", statusMsg);
|
||||
|
||||
|
||||
// tell the Process to run and wait for the results. The
|
||||
// return code (returnCode) will tell us whether the Temboo client
|
||||
// was able to send our request to the Temboo servers
|
||||
unsigned int returnCode = SetStatusChoreo.run();
|
||||
|
||||
// print the response code and API response.
|
||||
Serial.println("Response code: " + String(returnCode));
|
||||
|
||||
// note that in this case, we're just printing the raw response from Facebook.
|
||||
// see the examples on using Temboo SDK output filters at http://www.temboo.com/arduino
|
||||
// for information on how to filter this data
|
||||
while(SetStatusChoreo.available()) {
|
||||
char c = SetStatusChoreo.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
|
||||
SetStatusChoreo.close();
|
||||
}
|
||||
|
||||
Serial.println("Waiting...");
|
||||
Serial.println("");
|
||||
|
||||
delay(30000); // wait 30 seconds between SetStatus calls
|
||||
}
|
||||
|
||||
/*
|
||||
IMPORTANT NOTE: TembooAccount.h:
|
||||
|
||||
TembooAccount.h is a file referenced by this sketch that contains your Temboo account information.
|
||||
You'll need to edit the placeholder version of TembooAccount.h included with this example sketch,
|
||||
by inserting your own Temboo account name and app key information. The contents of the file should
|
||||
look like:
|
||||
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
You can find your Temboo App Key information on the Temboo website,
|
||||
under My Account > Application Keys
|
||||
|
||||
The same TembooAccount.h file settings can be used for all Temboo SDK sketches.
|
||||
|
||||
Keeping your account information in a separate file means you can share the main .ino file without worrying
|
||||
that you forgot to delete your credentials.
|
||||
*/
|
||||
@@ -0,0 +1,5 @@
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
UploadToDropbox
|
||||
|
||||
Demonstrates uploading a file to a Dropbox account using Temboo from an Arduino Yún.
|
||||
|
||||
Check out the latest Arduino & Temboo examples and support docs at http://www.temboo.com/arduino
|
||||
|
||||
A Temboo account and application key are necessary to run all Temboo examples.
|
||||
If you don't already have one, you can register for a free Temboo account at
|
||||
http://www.temboo.com
|
||||
|
||||
You'll also need a valid Dropbox app and accompanying OAuth credentials.
|
||||
To create a Dropbox app, visit https://www.dropbox.com/developers/apps and
|
||||
do the following:
|
||||
|
||||
1. Create a "Dropbox API app"
|
||||
2. Select "Files and datastores"
|
||||
3. Select "Yes - my app only needs access to the files it creates."
|
||||
|
||||
Once you've created your app, follow the instructions at
|
||||
https://www.temboo.com/library/Library/Dropbox/OAuth/ to run the Initialize and Finalize
|
||||
OAuth Choreos. These Choreos complete the OAuth handshake and retrieve your Dropbox OAuth access tokens.
|
||||
|
||||
This example assumes basic familiarity with Arduino sketches, and that your Yún is connected
|
||||
to the Internet.
|
||||
|
||||
Looking for another API to use with your Arduino Yún? We've got over 100 in our Library!
|
||||
|
||||
This example code is in the public domain.
|
||||
*/
|
||||
|
||||
#include <Bridge.h>
|
||||
#include <Temboo.h>
|
||||
#include "TembooAccount.h" // contains Temboo account information
|
||||
// as described in the footer comment below
|
||||
|
||||
|
||||
/*** SUBSTITUTE YOUR VALUES BELOW: ***/
|
||||
|
||||
// Note that for additional security and reusability, you could
|
||||
// use #define statements to specify these values in a .h file.
|
||||
|
||||
// your Dropbox app key, available on the Dropbox developer console after registering an app
|
||||
const String DROPBOX_APP_KEY = "xxxxxxxxxx";
|
||||
|
||||
// your Dropbox app secret, available on the Dropbox developer console after registering an app
|
||||
const String DROPBOX_APP_SECRET = "xxxxxxxxxx";
|
||||
|
||||
// your Dropbox access token, which is returned by the FinalizeOAuth Choreo
|
||||
const String DROPBOX_ACCESS_TOKEN = "xxxxxxxxxx";
|
||||
|
||||
// your Dropbox access token secret, which is returned by the FinalizeOAuth Choreo
|
||||
const String DROPBOX_ACCESS_TOKEN_SECRET = "xxxxxxxxxx";
|
||||
|
||||
|
||||
boolean success = false; // a flag to indicate whether we've uploaded the file yet
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// For debugging, wait until a serial console is connected.
|
||||
delay(4000);
|
||||
while(!Serial);
|
||||
Bridge.begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// only try to upload the file if we haven't already done so
|
||||
if (!success) {
|
||||
|
||||
Serial.println("Base64 encoding data to upload...");
|
||||
|
||||
// base64 encode the data to upload
|
||||
String base64EncodedData = base64Encode("Hello, Arduino!");
|
||||
|
||||
|
||||
Serial.println("Uploading data to Dropbox...");
|
||||
|
||||
// we need a Process object to send a Choreo request to Temboo
|
||||
TembooChoreo UploadFileChoreo;
|
||||
|
||||
// invoke the Temboo client
|
||||
// NOTE that the client must be reinvoked and repopulated with
|
||||
// appropriate arguments each time its run() method is called.
|
||||
UploadFileChoreo.begin();
|
||||
|
||||
// set Temboo account credentials
|
||||
UploadFileChoreo.setAccountName(TEMBOO_ACCOUNT);
|
||||
UploadFileChoreo.setAppKeyName(TEMBOO_APP_KEY_NAME);
|
||||
UploadFileChoreo.setAppKey(TEMBOO_APP_KEY);
|
||||
|
||||
// identify the Temboo Library choreo to run (Dropbox > FilesAndMetadata > UploadFile)
|
||||
UploadFileChoreo.setChoreo("/Library/Dropbox/FilesAndMetadata/UploadFile");
|
||||
|
||||
// set the required choreo inputs
|
||||
// see https://www.temboo.com/library/Library/Dropbox/FilesAndMetadata/UploadFile/
|
||||
// for complete details about the inputs for this Choreo
|
||||
|
||||
// first specify the name of the file to create/update on Dropbox
|
||||
UploadFileChoreo.addInput("FileName", "ArduinoTest.txt");
|
||||
|
||||
// next, the root folder on Dropbox relative to which the file path is specified.
|
||||
// to work with the Dropbox app you created earlier, this should be left as "sandbox"
|
||||
// if your Dropbox app has full access to your files, specify "dropbox"
|
||||
UploadFileChoreo.addInput("Root","sandbox");
|
||||
|
||||
// next, the Base64 encoded file data to upload
|
||||
UploadFileChoreo.addInput("FileContents", base64EncodedData);
|
||||
|
||||
// finally, the Dropbox OAuth credentials defined above
|
||||
UploadFileChoreo.addInput("AppSecret", DROPBOX_APP_SECRET);
|
||||
UploadFileChoreo.addInput("AccessToken", DROPBOX_ACCESS_TOKEN);
|
||||
UploadFileChoreo.addInput("AccessTokenSecret", DROPBOX_ACCESS_TOKEN_SECRET);
|
||||
UploadFileChoreo.addInput("AppKey", DROPBOX_APP_KEY);
|
||||
|
||||
// tell the Process to run and wait for the results. The
|
||||
// return code (returnCode) will tell us whether the Temboo client
|
||||
// was able to send our request to the Temboo servers
|
||||
unsigned int returnCode = UploadFileChoreo.run();
|
||||
|
||||
// a return code of zero (0) means everything worked
|
||||
if (returnCode == 0) {
|
||||
Serial.println("Success! File uploaded!");
|
||||
success = true;
|
||||
} else {
|
||||
// a non-zero return code means there was an error
|
||||
Serial.println("Uh-oh! Something went wrong!");
|
||||
}
|
||||
|
||||
// print out the full response to the serial monitor in all
|
||||
// cases, just for debugging
|
||||
while (UploadFileChoreo.available()) {
|
||||
char c = UploadFileChoreo.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
UploadFileChoreo.close();
|
||||
|
||||
Serial.println("Waiting...");
|
||||
}
|
||||
|
||||
delay(30000); // wait 30 seconds between upload attempts
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
A utility function to Base64 encode the specified string
|
||||
by calling a Temboo Utilities Choreo.
|
||||
*/
|
||||
String base64Encode(String toEncode) {
|
||||
|
||||
// we need a Process object to send a Choreo request to Temboo
|
||||
TembooChoreo Base64EncodeChoreo;
|
||||
|
||||
// invoke the Temboo client
|
||||
Base64EncodeChoreo.begin();
|
||||
|
||||
// set Temboo account credentials
|
||||
Base64EncodeChoreo.setAccountName(TEMBOO_ACCOUNT);
|
||||
Base64EncodeChoreo.setAppKeyName(TEMBOO_APP_KEY_NAME);
|
||||
Base64EncodeChoreo.setAppKey(TEMBOO_APP_KEY);
|
||||
|
||||
// identify the Temboo Library choreo to run (Utilities > Encoding > Base64Encode)
|
||||
Base64EncodeChoreo.setChoreo("/Library/Utilities/Encoding/Base64Encode");
|
||||
|
||||
// set choreo inputs
|
||||
Base64EncodeChoreo.addInput("Text", toEncode);
|
||||
|
||||
// run the choreo
|
||||
Base64EncodeChoreo.run();
|
||||
|
||||
// read in the choreo results, and return the "Base64EncodedText" output value.
|
||||
// see http://www.temboo.com/arduino for more details on using choreo outputs.
|
||||
while(Base64EncodeChoreo.available()) {
|
||||
// read the name of the output item
|
||||
String name = Base64EncodeChoreo.readStringUntil('\x1F');
|
||||
name.trim();
|
||||
|
||||
// read the value of the output item
|
||||
String data = Base64EncodeChoreo.readStringUntil('\x1E');
|
||||
data.trim();
|
||||
|
||||
if(name == "Base64EncodedText") {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
IMPORTANT NOTE: TembooAccount.h:
|
||||
|
||||
TembooAccount.h is a file referenced by this sketch that contains your Temboo account information.
|
||||
You'll need to edit the placeholder version of TembooAccount.h included with this example sketch,
|
||||
by inserting your own Temboo account name and app key information. The contents of the file should
|
||||
look like:
|
||||
|
||||
#define TEMBOO_ACCOUNT "myTembooAccountName" // your Temboo account name
|
||||
#define TEMBOO_APP_KEY_NAME "myFirstApp" // your Temboo app key name
|
||||
#define TEMBOO_APP_KEY "xxx-xxx-xxx-xx-xxx" // your Temboo app key
|
||||
|
||||
You can find your Temboo App Key information on the Temboo website,
|
||||
under My Account > Application Keys
|
||||
|
||||
The same TembooAccount.h file settings can be used for all Temboo SDK sketches.
|
||||
|
||||
Keeping your account information in a separate file means you can share the main .ino file without worrying
|
||||
that you forgot to delete your credentials.
|
||||
*/
|
||||
@@ -0,0 +1,99 @@
|
||||
// Copyright 2017, Temboo Inc.
|
||||
|
||||
#include <Process.h>
|
||||
|
||||
void setup() {
|
||||
// initialize the Bridge
|
||||
Bridge.begin();
|
||||
Serial.begin(9600);
|
||||
Process p;
|
||||
|
||||
//intro message
|
||||
Serial.println("**** Temboo Cloud Controls ****\n");
|
||||
|
||||
// update the package list
|
||||
Serial.print("Updating package listings...");
|
||||
p.runShellCommand("opkg update");
|
||||
int returnCode = p.exitValue();
|
||||
if (returnCode == 0) {
|
||||
Serial.println("Success!");
|
||||
} else {
|
||||
Serial.println("Failed. Make sure your device is connected to the internet properly.");
|
||||
while(p.available()) {
|
||||
char c = p.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Serial.println();
|
||||
// upgrade the Temboo package
|
||||
Serial.print("Updating Temboo...");
|
||||
p.runShellCommand("opkg install http://downloads.arduino.cc/openwrtyun/1/packages/temboo_1.3.1-1_ar71xx.ipk --force-depends");
|
||||
returnCode = p.exitValue();
|
||||
if (returnCode == 0) {
|
||||
Serial.println("Success!");
|
||||
} else {
|
||||
Serial.println("Failed.");
|
||||
Serial.println("Error number: " +String(returnCode));
|
||||
while(p.available()) {
|
||||
char c = p.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// install python openssl to allow for for ssl connections
|
||||
Serial.print("Installing python-openssl...");
|
||||
p.runShellCommand("opkg install python-openssl");
|
||||
returnCode = p.exitValue();
|
||||
if (returnCode == 0) {
|
||||
Serial.println("Success!");
|
||||
} else {
|
||||
Serial.println("Failed.");
|
||||
while(p.available()) {
|
||||
char c = p.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// Installing twisted web to work with CoAP gateway
|
||||
Serial.print("Installing twisted-web...");
|
||||
p.runShellCommand("opkg install http://downloads.arduino.cc/openwrtyun/1/packages/twisted-web_2.5.0-1_ar71xx.ipk --force-depends");
|
||||
returnCode = p.exitValue();
|
||||
if (returnCode == 0) {
|
||||
Serial.println("Success!");
|
||||
} else {
|
||||
Serial.println("Failed.");
|
||||
while(p.available()) {
|
||||
char c = p.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// Configuring zope
|
||||
Serial.print("Configuring zope...");
|
||||
p.runShellCommand("opkg install http://downloads.arduino.cc/openwrtyun/1/packages/zope-interface_2.5.0-1_ar71xx.ipk --force-depends");
|
||||
returnCode = p.exitValue();
|
||||
if (returnCode == 0) {
|
||||
p.runShellCommand("touch /usr/lib/python2.7/site-packages/zope/__init__.py");
|
||||
Serial.println("Success!");
|
||||
} else {
|
||||
Serial.println("Failed.");
|
||||
while(p.available()) {
|
||||
char c = p.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("Update Complete - your Yun is ready for Cloud Controls!");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// do nothing
|
||||
}
|
||||
48
arduino/libraries/Temboo/keywords.txt
Normal file
48
arduino/libraries/Temboo/keywords.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map Temboo
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Class (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
Temboo KEYWORD1
|
||||
TembooMQTTEdgeDevice KEYWORD1
|
||||
TembooCoAPEdgeDevice KEYWORD1
|
||||
TembooYunShield KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
TembooChoreo KEYWORD2
|
||||
TembooCoAPChoreo KEYWORD2
|
||||
TembooMQTTChoreo KEYWORD2
|
||||
TembooYunShieldChoreo KEYWORD2
|
||||
TembooDS18B20Config KEYWORD2
|
||||
TembooGPIOConfig KEYWORD2
|
||||
TembooSensor KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
setAccountName KEYWORD2
|
||||
setAppKeyName KEYWORD2
|
||||
setAppKey KEYWORD2
|
||||
setChoreo KEYWORD2
|
||||
setCredential KEYWORD2
|
||||
setSavedInputs KEYWORD2
|
||||
addInput KEYWORD2
|
||||
addOutputFilter KEYWORD2
|
||||
addSensorInput KEYWORD2
|
||||
addInputExpression KEYWORD2
|
||||
setDeviceName KEYWORD2
|
||||
setDeviceType KEYWORD2
|
||||
setSettingsFileToWrite KEYWORD2
|
||||
setSettingsFileToRead KEYWORD2
|
||||
setGatewayAddress KEYWORD2
|
||||
tembooDS18B20Init KEYWORD2
|
||||
tembooDigitalGPIOInit KEYWORD2
|
||||
tembooAnalogGPIOInit KEYWORD2
|
||||
10
arduino/libraries/Temboo/library.properties
Normal file
10
arduino/libraries/Temboo/library.properties
Normal file
@@ -0,0 +1,10 @@
|
||||
name=Temboo
|
||||
author=Temboo
|
||||
maintainer=Temboo <support@temboo.com>
|
||||
sentence=This library enables calls to Temboo, a platform that connects Arduino/Genuino boards to 100+ APIs, databases, code utilities and more.
|
||||
paragraph=Use this library to connect your Arduino or Genuino board to Temboo, making it simple to interact with a vast array of web-based resources and services.
|
||||
category=Communication
|
||||
url=http://www.temboo.com/arduino
|
||||
architectures=*
|
||||
version=1.2.1
|
||||
core-dependencies=arduino (>=1.5.0)
|
||||
433
arduino/libraries/Temboo/src/Temboo.cpp
Normal file
433
arduino/libraries/Temboo/src/Temboo.cpp
Normal file
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
|
||||
#if defined (ARDUINO_AVR_YUN) || defined (ARDUINO_AVR_TRE)
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// ARDUINO YUN AND TRE SUPPORT IN HEADER FILE
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#else //ARDUINO_AVR_YUN
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// BEGIN ARDUINO NON-YUN SUPPORT
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#include <string.h>
|
||||
#include <Client.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include <Temboo.h>
|
||||
#include "utility/TembooGlobal.h"
|
||||
#include "utility/TembooSession.h"
|
||||
|
||||
static const char HTTP_CODE[] PROGMEM = "HTTP_CODE\x0A\x1F";
|
||||
static char HTTP_EOL[] = "\r\n";
|
||||
static char HTTP_EOH[] = "\r\n\r\n";
|
||||
|
||||
TembooChoreo::TembooChoreo(Client& client) : m_client(client) {
|
||||
m_accountName = NULL;
|
||||
m_appKeyName = NULL;
|
||||
m_appKeyValue = NULL;
|
||||
m_path = NULL;
|
||||
m_nextChar = NULL;
|
||||
m_nextState = END;
|
||||
}
|
||||
|
||||
void TembooChoreo::setAccountName(const String& accountName) {
|
||||
m_accountName = accountName.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setAccountName(const char* accountName) {
|
||||
m_accountName = accountName;
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setAppKeyName(const String& appKeyName) {
|
||||
m_appKeyName = appKeyName.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setAppKeyName(const char* appKeyName) {
|
||||
m_appKeyName = appKeyName;
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setAppKey(const String& appKeyValue) {
|
||||
m_appKeyValue = appKeyValue.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setAppKey(const char* appKeyValue) {
|
||||
m_appKeyValue = appKeyValue;
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setChoreo(const String& path) {
|
||||
m_path = path.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setChoreo(const char* path) {
|
||||
m_path = path;
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setSavedInputs(const String& savedInputsName) {
|
||||
m_preset.put(savedInputsName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setSavedInputs(const char* savedInputsName) {
|
||||
m_preset.put(savedInputsName);
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setCredential(const String& credentialName) {
|
||||
m_preset.put(credentialName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setCredential(const char* credentialName) {
|
||||
m_preset.put(credentialName);
|
||||
}
|
||||
|
||||
void TembooChoreo::setProfile(const String& profileName) {
|
||||
m_preset.put(profileName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setProfile(const char* profileName) {
|
||||
m_preset.put(profileName);
|
||||
}
|
||||
|
||||
void TembooChoreo::setDeviceType(const String& deviceType) {
|
||||
m_deviceType.put(deviceType.c_str());
|
||||
}
|
||||
|
||||
void TembooChoreo::setDeviceType(const char* deviceType) {
|
||||
m_deviceType.put(deviceType);
|
||||
}
|
||||
|
||||
void TembooChoreo::setDeviceName(const String& deviceName) {
|
||||
m_deviceName.put(deviceName.c_str());
|
||||
}
|
||||
|
||||
void TembooChoreo::setDeviceName(const char* deviceName) {
|
||||
m_deviceName.put(deviceName);
|
||||
}
|
||||
|
||||
void TembooChoreo::addInput(const String& inputName, const String& inputValue) {
|
||||
m_inputs.put(inputName.c_str(), inputValue.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addInput(const char* inputName, const char* inputValue) {
|
||||
m_inputs.put(inputName, inputValue);
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addInput(const char* inputName, const String& inputValue) {
|
||||
m_inputs.put(inputName, inputValue.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addInput(const String& inputName, const char* inputValue) {
|
||||
m_inputs.put(inputName.c_str(), inputValue);
|
||||
}
|
||||
|
||||
void TembooChoreo::addInputWithSensor(const String& inputName, const String& inputValue) {
|
||||
m_expressions.put(inputName.c_str(), inputValue.c_str());
|
||||
}
|
||||
|
||||
void TembooChoreo::addInputWithSensor(const char* inputName, const String& inputValue) {
|
||||
m_expressions.put(inputName, inputValue.c_str());
|
||||
}
|
||||
|
||||
void TembooChoreo::addInputWithSensor(const char* inputName, const char* inputValue) {
|
||||
m_expressions.put(inputName, inputValue);
|
||||
}
|
||||
|
||||
void TembooChoreo::addInputExpression(const String& inputName, const String& inputValue) {
|
||||
m_expressions.put(inputName.c_str(), inputValue.c_str());
|
||||
}
|
||||
|
||||
void TembooChoreo::addInputExpression(const char* inputName, const String& inputValue) {
|
||||
m_expressions.put(inputName, inputValue.c_str());
|
||||
}
|
||||
|
||||
void TembooChoreo::addInputExpression(const char* inputName, const char* inputValue) {
|
||||
m_expressions.put(inputName, inputValue);
|
||||
}
|
||||
|
||||
void TembooChoreo::addSensorValue(const char* sensorName, int sensorValue, const char* conversion) {
|
||||
m_sensors.put(sensorName, sensorValue, conversion, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void TembooChoreo::addSensorValue(const char* sensorName, int sensorValue) {
|
||||
m_sensors.put(sensorName, sensorValue, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void TembooChoreo::addSensorValue(const char* sensorName, int sensorValue, const char* conversion, const char* calibrationValue) {
|
||||
m_sensors.put(sensorName, sensorValue, conversion, NULL, NULL, NULL, NULL, calibrationValue);
|
||||
}
|
||||
|
||||
void TembooChoreo::addSensorValue(const char* sensorName, int sensorValue, const char* rawLow, const char* rawHigh, const char* scaleLow, const char* scaleHigh) {
|
||||
m_sensors.put(sensorName, sensorValue, NULL, rawLow, rawHigh, scaleLow, scaleHigh, NULL);
|
||||
}
|
||||
|
||||
void TembooChoreo::addSensorInput(const char* sensorName, int sensorValue, const char* conversion) {
|
||||
m_sensors.put(sensorName, sensorValue, conversion, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void TembooChoreo::addSensorInput(const char* sensorName, int sensorValue) {
|
||||
m_sensors.put(sensorName, sensorValue, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void TembooChoreo::addSensorInput(const char* sensorName, int sensorValue, const char* conversion, const char* calibrationValue) {
|
||||
m_sensors.put(sensorName, sensorValue, conversion, NULL, NULL, NULL, NULL, calibrationValue);
|
||||
}
|
||||
|
||||
void TembooChoreo::addSensorInput(const char* sensorName, int sensorValue, const char* rawLow, const char* rawHigh, const char* scaleLow, const char* scaleHigh) {
|
||||
m_sensors.put(sensorName, sensorValue, NULL, rawLow, rawHigh, scaleLow, scaleHigh, NULL);
|
||||
}
|
||||
|
||||
void TembooChoreo::addOutputFilter(const char* outputName, const char* filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName, filterPath, variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addOutputFilter(const String& outputName, const char* filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath, variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addOutputFilter(const char* outputName, const String& filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName, filterPath.c_str(), variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addOutputFilter(const String& outputName, const String& filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath.c_str(), variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addOutputFilter(const char* outputName, const char* filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName, filterPath, variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addOutputFilter(const String& outputName, const char* filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath, variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addOutputFilter(const char* outputName, const String& filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName, filterPath.c_str(), variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addOutputFilter(const String& outputName, const String& filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath.c_str(), variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
int TembooChoreo::run() {
|
||||
return run(INADDR_NONE, 80, TEMBOO_CHOREO_DEFAULT_TIMEOUT_SECS);
|
||||
}
|
||||
|
||||
int TembooChoreo::run(uint16_t timeoutSecs) {
|
||||
return run(INADDR_NONE, 80, timeoutSecs);
|
||||
}
|
||||
|
||||
int TembooChoreo::run(IPAddress addr, uint16_t port) {
|
||||
return run(addr, port, TEMBOO_CHOREO_DEFAULT_TIMEOUT_SECS);
|
||||
}
|
||||
|
||||
int TembooChoreo::run(IPAddress addr, uint16_t port, uint16_t timeoutSecs) {
|
||||
|
||||
m_nextChar = NULL;
|
||||
|
||||
if (m_accountName == NULL || *m_accountName == '\0') {
|
||||
return TEMBOO_ERROR_ACCOUNT_MISSING;
|
||||
}
|
||||
|
||||
if (m_path == NULL || *m_path == '\0') {
|
||||
return TEMBOO_ERROR_CHOREO_MISSING;
|
||||
}
|
||||
|
||||
if (m_appKeyName == NULL || *m_appKeyName == '\0') {
|
||||
return TEMBOO_ERROR_APPKEY_NAME_MISSING;
|
||||
}
|
||||
|
||||
if (m_appKeyValue == NULL || *m_appKeyValue == '\0') {
|
||||
return TEMBOO_ERROR_APPKEY_MISSING;
|
||||
}
|
||||
|
||||
TembooSession session(m_client, addr, port);
|
||||
uint16_t httpCode = 0;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
unsigned long timeoutBeginSecs = session.getTime();
|
||||
if (0 != session.executeChoreo(m_accountName, m_appKeyName, m_appKeyValue, m_path, m_inputs, m_expressions, m_sensors, m_outputs, m_preset, m_deviceType, m_deviceName)) {
|
||||
httpCode = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
while(!m_client.available()) {
|
||||
if((session.getTime() - timeoutBeginSecs) >= timeoutSecs) {
|
||||
TEMBOO_TRACELN("Receive time out");
|
||||
m_client.stop();
|
||||
return TEMBOO_ERROR_STREAM_TIMEOUT;
|
||||
}
|
||||
if (!m_client.connected()) {
|
||||
TEMBOO_TRACELN("Disconnected");
|
||||
return TEMBOO_ERROR_HTTP_ERROR;
|
||||
}
|
||||
delay(10);
|
||||
}
|
||||
if (!m_client.findUntil("HTTP/1.", HTTP_EOL)) {
|
||||
TEMBOO_TRACELN("No HTTP");
|
||||
return TEMBOO_ERROR_HTTP_ERROR;
|
||||
}
|
||||
//Don't care if the next byte is a '1' or a '0'
|
||||
m_client.read();
|
||||
|
||||
//Read the HTTP status code
|
||||
httpCode = (uint16_t)m_client.parseInt();
|
||||
|
||||
// We expect HTTP response codes to be <= 599, but
|
||||
// we need to be prepared for anything.
|
||||
if (httpCode >= 600) {
|
||||
TEMBOO_TRACELN("Invalid HTTP");
|
||||
httpCode = 0;
|
||||
}
|
||||
|
||||
// if we get an auth error AND there was an x-temboo-time header,
|
||||
// update the session timeOffset
|
||||
if ((httpCode == 401) && (i == 0)) {
|
||||
if (m_client.findUntil("x-temboo-time:", HTTP_EOH)) {
|
||||
TembooSession::setTime((unsigned long)m_client.parseInt());
|
||||
while(m_client.available()) {
|
||||
m_client.read();
|
||||
}
|
||||
m_client.stop();
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint16toa(httpCode, m_httpCodeStr);
|
||||
strcat_P(m_httpCodeStr, PSTR("\x0A\x1E"));
|
||||
m_nextState = START;
|
||||
m_nextChar = HTTP_CODE;
|
||||
|
||||
if (httpCode < 200 || httpCode >= 300) {
|
||||
return TEMBOO_ERROR_HTTP_ERROR;
|
||||
}
|
||||
|
||||
if (!m_client.find(HTTP_EOH)) {
|
||||
return TEMBOO_ERROR_HTTP_ERROR;
|
||||
}
|
||||
|
||||
return TEMBOO_ERROR_OK;
|
||||
}
|
||||
|
||||
void TembooChoreo::close() {
|
||||
m_client.stop();
|
||||
}
|
||||
|
||||
int TembooChoreo::available() {
|
||||
// If we're still sending the HTTP response code,
|
||||
// report at least one character available.
|
||||
if (m_nextChar != NULL) {
|
||||
return m_client.available() + 1;
|
||||
}
|
||||
|
||||
// Otherwise, return however many characters the client has.
|
||||
return m_client.available();
|
||||
}
|
||||
|
||||
|
||||
int TembooChoreo::peek() {
|
||||
// If we're still sending the HTTP response code,
|
||||
// return the next character in that sequence.
|
||||
if (m_nextChar != NULL) {
|
||||
if(m_nextState != HTTP_CODE_VALUE) {
|
||||
return (int)pgm_read_byte(m_nextChar);
|
||||
} else {
|
||||
return (int)*m_nextChar;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, return whatever is in the client buffer.
|
||||
return m_client.peek();
|
||||
}
|
||||
|
||||
|
||||
int TembooChoreo::read() {
|
||||
|
||||
int c = 0;
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
m_nextChar = HTTP_CODE;
|
||||
c = (int)pgm_read_byte(m_nextChar++);
|
||||
m_nextState = HTTP_CODE_TAG;
|
||||
break;
|
||||
|
||||
case HTTP_CODE_TAG:
|
||||
c = (int)pgm_read_byte(m_nextChar++);
|
||||
if (pgm_read_byte(m_nextChar) == '\0') {
|
||||
m_nextState = HTTP_CODE_VALUE;
|
||||
m_nextChar = m_httpCodeStr;
|
||||
}
|
||||
break;
|
||||
|
||||
case HTTP_CODE_VALUE:
|
||||
c = (int)(*m_nextChar++);
|
||||
if (*m_nextChar == '\0') {
|
||||
m_nextState = END;
|
||||
m_nextChar = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
c = m_client.read();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
size_t TembooChoreo::write(uint8_t data) {
|
||||
return m_client.write(data);
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::flush() {
|
||||
m_nextChar = NULL;
|
||||
m_nextState = END;
|
||||
m_client.flush();
|
||||
}
|
||||
|
||||
#endif //ARDUINO_AVR_YUN
|
||||
225
arduino/libraries/Temboo/src/Temboo.h
Normal file
225
arduino/libraries/Temboo/src/Temboo.h
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOO_H_
|
||||
#define TEMBOO_H_
|
||||
|
||||
#ifndef TEMBOO_LIBRARY_VERSION
|
||||
#define TEMBOO_LIBRARY_VERSION 2
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#if defined (ARDUINO_AVR_YUN) || defined (ARDUINO_AVR_TRE)
|
||||
///////////////////////////////////////////////////////
|
||||
// BEGIN ARDUINO YUN AND TRE SUPPORT
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#include <Process.h>
|
||||
|
||||
class TembooChoreo : public Process {
|
||||
|
||||
public:
|
||||
void begin() {Process::begin("temboo");}
|
||||
void setAccountName(const String& accountName) { addParameter("-a" + accountName);}
|
||||
void setAppKeyName(const String& appKeyName) { addParameter("-u" + appKeyName);}
|
||||
void setAppKey(const String& appKey) { addParameter("-p" + appKey);}
|
||||
void setChoreo(const String& choreo) { addParameter("-c" + choreo);}
|
||||
void setCredential(const String& credentialName) { addParameter("-e" + credentialName);}
|
||||
void setSavedInputs(const String& savedInputsName) { addParameter("-e" + savedInputsName);}
|
||||
void setProfile(const String& profileName) { addParameter("-e" + profileName);}
|
||||
void addInput(const String& inputName, const String& inputValue) { addParameter("-i" + inputName + ":" + inputValue);}
|
||||
void addOutputFilter(const String& filterName, const String& filterPath, const String& variableName) { addParameter("-o" + filterName + ":" + filterPath + ":" + variableName);}
|
||||
void setSettingsFileToWrite(const String& filePath) { addParameter("-w" + filePath);}
|
||||
void setSettingsFileToRead(const String& filePath) { addParameter("-r" + filePath);}
|
||||
void setGatewayAddress(const String& addr) { addParameter("-s" + addr);}
|
||||
void addInputExpression(const String& inputName, const String& inputValue) { addParameter("-f" + inputName + ":" + inputValue);}
|
||||
void addInputWithSensor(const String& inputName, const String& inputValue) { addParameter("-f" + inputName + ":" + inputValue);}
|
||||
void addSensorInput(const String& sensorName, long sensorValue, const String& conversion) {addParameter("-n" + sensorName + ":" + String(sensorValue) + ":" + conversion);}
|
||||
void addSensorInput(const String& sensorName, long sensorValue) {addParameter("-v" + sensorName + ":" + String(sensorValue));}
|
||||
void addSensorInput(const String& sensorName, long sensorValue, const String& conversion, const String& calibrationValue) {addParameter("-b" + sensorName + ":" + String(sensorValue) + ":" + conversion + ":" + calibrationValue);}
|
||||
void addSensorInput(const String& sensorName, long sensorValue, const String& rawLow, const String& rawHigh, const String& scaleLow, const String& scaleHigh) {addParameter("-m" + sensorName + ":" + String(sensorValue) + ":" + rawLow+ ":" + rawHigh+ ":" + scaleLow+ ":" + scaleHigh);}
|
||||
void addSensorValue(const String& sensorName, long sensorValue, const String& conversion) {addParameter("-n" + sensorName + ":" + String(sensorValue) + ":" + conversion);}
|
||||
void addSensorValue(const String& sensorName, long sensorValue) {addParameter("-v" + sensorName + ":" + String(sensorValue));}
|
||||
void addSensorValue(const String& sensorName, long sensorValue, const String& conversion, const String& calibrationValue) {addParameter("-b" + sensorName + ":" + String(sensorValue) + ":" + conversion + ":" + calibrationValue);}
|
||||
void addSensorValue(const String& sensorName, long sensorValue, const String& rawLow, const String& rawHigh, const String& scaleLow, const String& scaleHigh) {addParameter("-m" + sensorName + ":" + String(sensorValue) + ":" + rawLow+ ":" + rawHigh+ ":" + scaleLow+ ":" + scaleHigh);}
|
||||
void setDeviceName(const String& deviceName) {addParameter("-d" + deviceName);}
|
||||
void setDeviceType(const String& deviceType) {addParameter("-t" + deviceType);}
|
||||
};
|
||||
|
||||
#else //ARDUINO_AVR_YUN
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// BEGIN ARDUINO NON-YUN SUPPORT
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#include <Stream.h>
|
||||
#include <Client.h>
|
||||
#include <IPAddress.h>
|
||||
#include "utility/ChoreoInputSet.h"
|
||||
#include "utility/ChoreoInputExpressionSet.h"
|
||||
#include "utility/ChoreoSensorInputSet.h"
|
||||
#include "utility/ChoreoOutputSet.h"
|
||||
#include "utility/ChoreoPreset.h"
|
||||
#include "utility/ChoreoDevice.h"
|
||||
|
||||
#define TEMBOO_ERROR_OK (0)
|
||||
#define TEMBOO_ERROR_ACCOUNT_MISSING (201)
|
||||
#define TEMBOO_ERROR_CHOREO_MISSING (203)
|
||||
#define TEMBOO_ERROR_APPKEY_NAME_MISSING (205)
|
||||
#define TEMBOO_ERROR_APPKEY_MISSING (207)
|
||||
#define TEMBOO_ERROR_HTTP_ERROR (223)
|
||||
#define TEMBOO_ERROR_STREAM_TIMEOUT (225)
|
||||
#define TEMBOO_CHOREO_DEFAULT_TIMEOUT_SECS (901) //15 minutes and 1 second
|
||||
|
||||
class TembooChoreo : public Stream {
|
||||
public:
|
||||
|
||||
// Constructor.
|
||||
// client - an instance of an Arduino Client, usually an EthernetClient
|
||||
// or a WiFiClient. Used to communicate with Temboo.
|
||||
TembooChoreo(Client& client);
|
||||
|
||||
// Does nothing. Just for source compatibility with Yun code.
|
||||
void begin() {};
|
||||
|
||||
// Sets the account name to use when communicating with Temboo.
|
||||
// (required)
|
||||
void setAccountName(const String& accountName);
|
||||
void setAccountName(const char* accountName);
|
||||
|
||||
// Sets the application key name to use with choreo execution requests.
|
||||
// (required)
|
||||
void setAppKeyName(const String& appKeyName);
|
||||
void setAppKeyName(const char* appKeyName);
|
||||
|
||||
// Sets the application key value to use with choreo execution requests
|
||||
// (required)
|
||||
void setAppKey(const String& appKey);
|
||||
void setAppKey(const char* appKey);
|
||||
|
||||
// Sets the name of the choreo to be executed.
|
||||
// (required)
|
||||
void setChoreo(const String& choreoPath);
|
||||
void setChoreo(const char* choreoPath);
|
||||
|
||||
// Sets the name of the saved inputs to use when executing the choreo
|
||||
// (optional)
|
||||
void setSavedInputs(const String& savedInputsName);
|
||||
void setSavedInputs(const char* savedInputsName);
|
||||
|
||||
void setCredential(const String& credentialName);
|
||||
void setCredential(const char* credentialName);
|
||||
|
||||
void setProfile(const String& profileName);
|
||||
void setProfile(const char* profileName);
|
||||
|
||||
void setDeviceType(const String& deviceType);
|
||||
void setDeviceType(const char* deviceType);
|
||||
|
||||
void setDeviceName(const String& deviceName);
|
||||
void setDeviceName(const char* deviceName);
|
||||
|
||||
// Sets an input to be used when executing a choreo.
|
||||
// (optional or required, depending on the choreo being executed.)
|
||||
void addInput(const String& inputName, const String& inputValue);
|
||||
void addInput(const char* inputName, const char* inputValue);
|
||||
void addInput(const char* inputName, const String& inputValue);
|
||||
void addInput(const String& inputName, const char* inputValue);
|
||||
|
||||
// Sets a Choreo input that contains a sensor value to be converted by Temboo
|
||||
void addInputWithSensor(const String& inputName, const String& inputValue);
|
||||
void addInputWithSensor(const char* inputName, const String& inputValue);
|
||||
void addInputWithSensor(const char* inputName, const char* inputValue);
|
||||
// Keeping legacy methods
|
||||
void addInputExpression(const String& inputName, const String& inputValue);
|
||||
void addInputExpression(const char* inputName, const String& inputValue);
|
||||
void addInputExpression(const char* inputName, const char* inputValue);
|
||||
|
||||
// Sets in input that is using a sensor value. Different parameters are needed depending
|
||||
// on the type of sensor being used.
|
||||
void addSensorValue(const char* sensorName, int sensorValue, const char* conversion);
|
||||
void addSensorValue(const char* sensorName, int sensorValue);
|
||||
void addSensorValue(const char* sensorName, int sensorValue, const char* conversion, const char* calibrationValue);
|
||||
void addSensorValue(const char* sensorName, int sensorValue, const char* rawLow, const char* rawHigh, const char* scaleLow, const char* scaleHigh);
|
||||
// Keeping legacy methods
|
||||
void addSensorInput(const char* sensorName, int sensorValue, const char* conversion);
|
||||
void addSensorInput(const char* sensorName, int sensorValue);
|
||||
void addSensorInput(const char* sensorName, int sensorValue, const char* conversion, const char* calibrationValue);
|
||||
void addSensorInput(const char* sensorName, int sensorValue, const char* rawLow, const char* rawHigh, const char* scaleLow, const char* scaleHigh);
|
||||
|
||||
// Sets an output filter to be used to process the choreo output
|
||||
// (optional)
|
||||
void addOutputFilter(const char* filterName, const char* filterPath, const char* variableName);
|
||||
void addOutputFilter(const String& filterName, const char* filterPath, const char* variableName);
|
||||
void addOutputFilter(const char* filterName, const String& filterPath, const char* variableName);
|
||||
void addOutputFilter(const String& filterName, const String& filterPath, const char* variableName);
|
||||
void addOutputFilter(const char* filterName, const char* filterPath, const String& variableName);
|
||||
void addOutputFilter(const String& filterName, const char* filterPath, const String& variableName);
|
||||
void addOutputFilter(const char* filterName, const String& filterPath, const String& variableName);
|
||||
void addOutputFilter(const String& filterName, const String& filterPath, const String& variableName);
|
||||
|
||||
// Run the choreo using the current input info
|
||||
int run();
|
||||
// Run the choreo with a user specified timeout
|
||||
int run(uint16_t timeoutSecs);
|
||||
|
||||
// Run the choreo on the Temboo server at the given IP address and port
|
||||
int run(IPAddress addr, uint16_t port);
|
||||
int run(IPAddress addr, uint16_t port, uint16_t timeoutSecs);
|
||||
|
||||
void close();
|
||||
|
||||
// Stream interface - see the Arduino library documentation.
|
||||
int available();
|
||||
int read();
|
||||
int peek();
|
||||
void flush();
|
||||
|
||||
//Print interface - see the Arduino library documentation
|
||||
size_t write(uint8_t data);
|
||||
|
||||
|
||||
protected:
|
||||
ChoreoInputSet m_inputs;
|
||||
ChoreoInputExpressionSet m_expressions;
|
||||
ChoreoSensorInputSet m_sensors;
|
||||
ChoreoOutputSet m_outputs;
|
||||
ChoreoPreset m_preset;
|
||||
ChoreoDevice m_deviceType;
|
||||
ChoreoDevice m_deviceName;
|
||||
|
||||
const char* m_accountName;
|
||||
const char* m_appKeyValue;
|
||||
const char* m_appKeyName;
|
||||
const char* m_path;
|
||||
Client& m_client;
|
||||
char m_httpCodeStr[6];
|
||||
const char* m_nextChar;
|
||||
enum State {START, HTTP_CODE_TAG, HTTP_CODE_VALUE, END};
|
||||
State m_nextState;
|
||||
|
||||
};
|
||||
|
||||
#endif //ARDUINO_AVR_YUN
|
||||
|
||||
#endif //TEMBOO_H_
|
||||
935
arduino/libraries/Temboo/src/TembooCoAPEdgeDevice.cpp
Normal file
935
arduino/libraries/Temboo/src/TembooCoAPEdgeDevice.cpp
Normal file
@@ -0,0 +1,935 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
|
||||
#include "utility/TembooGlobal.h"
|
||||
#include "utility/TembooCoAPSession.h"
|
||||
#include "utility/TembooTags.h"
|
||||
|
||||
#include "TembooCoAPEdgeDevice.h"
|
||||
|
||||
#ifndef UINT16_MAX
|
||||
#define UINT16_MAX (0xFFFF)
|
||||
#endif
|
||||
|
||||
#ifndef UINT32_MAX
|
||||
#define UINT32_MAX (0xFFFFFFFF)
|
||||
#endif
|
||||
|
||||
//TODO: Maybe. Put these in PROGMEM and
|
||||
// modify any code that uses them.
|
||||
const char HTTP_CODE_PREFIX[] = "HTTP_CODE\x0A\x1F";
|
||||
const char HTTP_CODE_SUFFIX[] = "\x0A\x1E";
|
||||
const char TembooCoAPClient::URI_PATH[] = "exec";
|
||||
const char TIME_URI_PATH[] = "time";
|
||||
static char HEADER_TIME[] = "x-temboo-time:";
|
||||
|
||||
uint16_t TembooCoAPChoreo::s_nextRequestId = 0;
|
||||
|
||||
TembooCoAPClient::TembooCoAPClient(TembooCoAPIPStack& ipStack, IPAddress gatewayAddress, uint16_t gatewayPort) :
|
||||
m_messageLayer(m_rxBuffer, sizeof(m_rxBuffer), ipStack),
|
||||
m_rrLayer(m_messageLayer, m_rxBuffer, sizeof(m_rxBuffer)),
|
||||
m_gatewayAddress(gatewayAddress),
|
||||
m_gatewayPort(gatewayPort),
|
||||
m_messageID(0),
|
||||
m_state(STATE_IDLE),
|
||||
m_blockSize(MAX_BLOCK_SIZE),
|
||||
m_lastError(NO_ERROR),
|
||||
m_dataLen(0),
|
||||
m_txIndex(0),
|
||||
m_respLen(0),
|
||||
m_txByteCount(0),
|
||||
m_respHttpCode(0) {
|
||||
|
||||
memset(m_token, 0, sizeof(m_token));
|
||||
memset(m_dataBuffer, 0, sizeof(m_dataBuffer));
|
||||
memset(m_respBuffer, 0, sizeof(m_respBuffer));
|
||||
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPClient::resetChoreo() {
|
||||
memset(m_token, 0, sizeof(m_token));
|
||||
memset(m_dataBuffer, 0, sizeof(m_dataBuffer));
|
||||
memset(m_respBuffer, 0, sizeof(m_respBuffer));
|
||||
m_dataLen = 0;
|
||||
m_respLen = 0;
|
||||
m_txIndex = 0;
|
||||
m_txByteCount = 0;
|
||||
m_rxBlockNum = 0;
|
||||
|
||||
m_rrLayer.setState(CoapRRLayer::STATE_IDLE);
|
||||
m_messageLayer.setState(CoapMessageLayer::STATE_CLOSED);
|
||||
}
|
||||
|
||||
void TembooCoAPClient::begin(long seed){
|
||||
//RFC7252 "strongly recommends" that the initial
|
||||
//value of messageID be randomized. There's no good way
|
||||
//to do that reliably on many MCU boards. We will
|
||||
//use random(), and can instruct the user to call randomSeed
|
||||
//with input from an unused analog input if it's important to them.
|
||||
randomSeed(seed);
|
||||
m_messageID = random(0, UINT16_MAX);
|
||||
|
||||
}
|
||||
|
||||
|
||||
TembooCoAPClient::~TembooCoAPClient() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
TembooCoAPClient::Result TembooCoAPClient::write(uint8_t value) {
|
||||
if (m_dataLen < sizeof(m_dataBuffer)) {
|
||||
m_dataBuffer[m_dataLen] = value;
|
||||
m_dataLen++;
|
||||
m_txByteCount = 0;
|
||||
m_txIndex = 0;
|
||||
return NO_ERROR;
|
||||
}
|
||||
return ERROR_BUFFER_FULL;
|
||||
}
|
||||
|
||||
TembooCoAPClient::Result TembooCoAPClient::saveResponse(uint8_t* values, uint16_t len) {
|
||||
len = len < (sizeof(m_respBuffer) - m_respLen - 1) ? len : (sizeof(m_respBuffer) - m_respLen -1);
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Saving payload to the buffer");
|
||||
if ( len > 0) {
|
||||
memcpy(&m_respBuffer[m_respLen], values, len);
|
||||
m_respLen += len;
|
||||
return NO_ERROR;
|
||||
}
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Buffer full, payload not saved");
|
||||
return ERROR_BUFFER_FULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
TembooCoAPClient::Result TembooCoAPClient::write(uint8_t* values, uint16_t len) {
|
||||
Result rc = NO_ERROR;
|
||||
while(NO_ERROR == rc && len > 0) {
|
||||
rc = write(*values++);
|
||||
len--;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint16_t TembooCoAPClient::getNextMessageID() {
|
||||
m_messageID++;
|
||||
if (m_messageID == 0) {
|
||||
m_messageID++;
|
||||
}
|
||||
return m_messageID;
|
||||
}
|
||||
|
||||
|
||||
TembooCoAPClient::Result TembooCoAPClient::generateToken() {
|
||||
// 5.3.1. Token suggests the tokenID should be a random value
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
m_token[i] = (rand() % 93) + 33;
|
||||
}
|
||||
m_token[8] = '\0';
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
TembooCoAPClient::Result TembooCoAPClient::sendBlockRequest(uint16_t msgID, uint32_t blockNum) {
|
||||
|
||||
CoapMsg msg(m_txBuffer, sizeof (m_txBuffer));
|
||||
msg.setCode(CoapMsg::COAP_POST);
|
||||
|
||||
if (msg.setToken((uint8_t*)m_token, strlen(m_token))) {
|
||||
TEMBOO_TRACELN("err: setToken");
|
||||
return ERROR_MSG_TOKEN;
|
||||
}
|
||||
|
||||
msg.setId(msgID);
|
||||
|
||||
if (msg.addOption(CoapMsg::COAP_OPTION_URI_PATH, (const uint8_t*)URI_PATH, strlen(URI_PATH))) {
|
||||
TEMBOO_TRACELN("err: setURI");
|
||||
return ERROR_MSG_OPTION;
|
||||
}
|
||||
|
||||
uint8_t optionValue[3];
|
||||
uint16_t optionLen = 0;
|
||||
|
||||
// If this is the last block in a series of blocks (or an only block)
|
||||
// include a block2 option to let the server know what our
|
||||
// desired block size is for the response.
|
||||
|
||||
optionValue[0] = (blockNum & 0xF000) >> 12;
|
||||
optionValue[1] = (blockNum & 0x0FF0) >> 4;
|
||||
optionValue[2] = (blockNum & 0x000F) << 4;
|
||||
optionValue[2] |= (0 ? 0x08 : 0);
|
||||
optionValue[2] |= (m_blockSize >> 5) & 0x07;
|
||||
|
||||
optionLen = 1;
|
||||
if (optionValue[0] > 0) {
|
||||
optionLen = 3;
|
||||
} else if (optionValue[1] > 0) {
|
||||
optionLen = 2;
|
||||
}
|
||||
if (msg.addOption(CoapMsg::COAP_OPTION_BLOCK2, (const uint8_t*)&optionValue[3 - optionLen], optionLen)) {
|
||||
TEMBOO_TRACELN("err: block2");
|
||||
return ERROR_MSG_OPTION;
|
||||
}
|
||||
|
||||
if (m_rrLayer.reliableSend(msg, m_token, m_gatewayAddress, m_gatewayPort) != CoapRRLayer::NO_ERROR) {
|
||||
TEMBOO_TRACELN("err: send");
|
||||
return ERROR_SENDING_MSG;
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
TembooCoAPClient::Result TembooCoAPClient::sendBlock(uint16_t msgID, uint8_t* payload, size_t len, uint32_t blockNum, bool moreBlocks) {
|
||||
|
||||
CoapMsg msg(m_txBuffer, sizeof (m_txBuffer));
|
||||
msg.setCode(CoapMsg::COAP_POST);
|
||||
|
||||
if (msg.setToken((uint8_t*)m_token, strlen(m_token))) {
|
||||
TEMBOO_TRACELN("err: setToken");
|
||||
return ERROR_MSG_TOKEN;
|
||||
}
|
||||
|
||||
msg.setId(msgID);
|
||||
|
||||
if (msg.addOption(CoapMsg::COAP_OPTION_URI_PATH, (const uint8_t*)URI_PATH, strlen(URI_PATH))) {
|
||||
TEMBOO_TRACELN("err: setURI");
|
||||
return ERROR_MSG_OPTION;
|
||||
}
|
||||
|
||||
uint8_t optionValue[3];
|
||||
uint16_t optionLen = 0;
|
||||
|
||||
// If this is the last block in a series of blocks (or an only block)
|
||||
// include a block2 option to let the server know what our
|
||||
// desired block size is for the response.
|
||||
if (!moreBlocksToSend()) {
|
||||
optionValue[0] = (m_blockSize >> 5) & 0x07;
|
||||
optionLen = (optionValue[0] > 0) ? 1 : 0;
|
||||
if (msg.addOption(CoapMsg::COAP_OPTION_BLOCK2, (const uint8_t*)optionValue, optionLen)) {
|
||||
TEMBOO_TRACELN("err: block2");
|
||||
return ERROR_MSG_OPTION;
|
||||
}
|
||||
}
|
||||
|
||||
// If this is not the only block in the request,
|
||||
// include the block1 option.
|
||||
if (blockNum > 0 || moreBlocks) {
|
||||
|
||||
optionValue[0] = (blockNum & 0xF000) >> 12;
|
||||
optionValue[1] = (blockNum & 0x0FF0) >> 4;
|
||||
optionValue[2] = (blockNum & 0x000F) << 4;
|
||||
optionValue[2] |= (moreBlocks ? 0x08 : 0);
|
||||
optionValue[2] |= (m_blockSize >> 5) & 0x07;
|
||||
|
||||
optionLen = 1;
|
||||
if (optionValue[0] > 0) {
|
||||
optionLen = 3;
|
||||
} else if (optionValue[1] > 0) {
|
||||
optionLen = 2;
|
||||
}
|
||||
|
||||
if (msg.addOption(CoapMsg::COAP_OPTION_BLOCK1, (const uint8_t*)&optionValue[3 - optionLen], optionLen)) {
|
||||
TEMBOO_TRACELN("err: block1");
|
||||
return ERROR_MSG_OPTION;
|
||||
}
|
||||
}
|
||||
|
||||
if (msg.setPayload((uint8_t*)payload, len)) {
|
||||
TEMBOO_TRACELN("err: setPayload");
|
||||
return ERROR_MSG_PAYLOAD;
|
||||
}
|
||||
|
||||
if (m_rrLayer.reliableSend(msg, m_token, m_gatewayAddress, m_gatewayPort) != CoapRRLayer::NO_ERROR) {
|
||||
TEMBOO_TRACELN("err: send");
|
||||
return ERROR_SENDING_MSG;
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPClient::adjustRequestBlockSize(CoapMsg& msg) {
|
||||
|
||||
// A block1 option in a response means the server is
|
||||
// requesting that we use a smaller block size.
|
||||
uint16_t newBlockSize = msg.getBlock1Size();
|
||||
if (newBlockSize > 0 && newBlockSize < m_blockSize) {
|
||||
m_blockSize = newBlockSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TembooCoAPClient::Result TembooCoAPClient::loop() {
|
||||
|
||||
m_lastResult = NO_ERROR;
|
||||
|
||||
switch (m_state) {
|
||||
|
||||
case STATE_IDLE:
|
||||
case STATE_RESPONSE_READY:
|
||||
// Pump the receiver.
|
||||
// We're not serving anything, so unless there's an outstanding
|
||||
// request (which would mean we would be in STATE_WAITING, not STATE_IDLE),
|
||||
// the R/R layer will reject or ignore any incoming traffic.
|
||||
m_rrLayer.loop();
|
||||
|
||||
break;
|
||||
case STATE_SEND_REQUEST:
|
||||
case STATE_RESPONSE_STARTED:
|
||||
case STATE_WAITING_FOR_RESPONSE:
|
||||
// We're waiting for a response to an earlier request.
|
||||
switch(m_rrLayer.loop()) {
|
||||
|
||||
case CoapRRLayer::NO_ERROR:
|
||||
// Nothing happened. Nothing to do.
|
||||
break;
|
||||
|
||||
case CoapRRLayer::RESPONSE_RECEIVED: {
|
||||
|
||||
// A response to our request was received.
|
||||
// It may have been a piggybacked ACK or a separate response
|
||||
CoapMsg msg(m_rxBuffer, sizeof(m_rxBuffer), m_messageLayer.getRXByteCount());
|
||||
|
||||
// See if it has a BLOCK1 option. If so, make sure the
|
||||
// block number matches the one we just sent. If the block
|
||||
// numbers don't match, we're FUBAR, so abort the request.
|
||||
// If they do match, adjust our request block size if the
|
||||
// server requested a different (smaller) size.
|
||||
|
||||
if (msg.getOptionCount(CoapMsg::COAP_OPTION_BLOCK1)) {
|
||||
uint32_t ackBlockNum = msg.getBlock1Num();
|
||||
if (ackBlockNum != m_txBlockNum) {
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Block1 message number does not match");
|
||||
m_lastResult = ERROR_RECEIVING_RESPONSE;
|
||||
if (msg.getType() == CoapMsg::COAP_CONFIRMABLE) {
|
||||
m_messageLayer.rejectMsg(msg);
|
||||
}
|
||||
resetChoreo();
|
||||
break;
|
||||
}
|
||||
adjustRequestBlockSize(msg);
|
||||
}
|
||||
|
||||
// Now deal with the response itself.
|
||||
switch(msg.getCode()) {
|
||||
case CoapMsg::COAP_CONTINUE: //2.31
|
||||
// 2.31 means the server is requesting the next block of the request.
|
||||
// If there are no more blocks to send, we're FUBAR, so abort the
|
||||
// request. Otherwise, send the next block.
|
||||
if (m_txIndex >= m_dataLen) {
|
||||
// no more data to send, bad news
|
||||
resetChoreo();
|
||||
m_lastResult = ERROR_REQUEST_FAILED;
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Gateway requested too many blocks");
|
||||
break;
|
||||
}
|
||||
if (sendChoreoRequest() != NO_ERROR) {
|
||||
resetChoreo();
|
||||
m_lastResult = ERROR_REQUEST_FAILED;
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Send Choreo request failed");
|
||||
}
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_REQUEST_ENTITY_INCOMPLETE: //4.08
|
||||
// 4.08 means the server is missing one or more blocks, so can't
|
||||
// service the request.
|
||||
// We're FUBAR, so abort the request.
|
||||
resetChoreo();
|
||||
m_lastResult = ERROR_REQUEST_FAILED;
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Gateway returned 4.08");
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_REQUEST_ENTITY_TOO_LARGE: //4.13
|
||||
// 4.13 means the server ran out of memory when receiving the
|
||||
// request.
|
||||
// We're FUBAR, so abort the request.
|
||||
resetChoreo();
|
||||
m_lastResult = ERROR_REQUEST_FAILED;
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Gateway returned 4.13");
|
||||
break;
|
||||
|
||||
default:
|
||||
// Any response code other than the special ones above means the
|
||||
// server has processed the request and is returning the final result,
|
||||
// which may be in one or more blocks. If we haven't finished sending
|
||||
// the request, we're FUBAR, so abort the request. Otherwise, process
|
||||
// the response.
|
||||
m_dataLen = 0;
|
||||
if (moreBlocksToSend()) {
|
||||
m_lastResult = ERROR_RECEIVING_RESPONSE;
|
||||
if (msg.getType() == CoapMsg::COAP_CONFIRMABLE) {
|
||||
m_messageLayer.rejectMsg(msg);
|
||||
}
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Response received before request finished");
|
||||
resetChoreo();
|
||||
m_state = STATE_IDLE;
|
||||
break;
|
||||
} else {
|
||||
if (msg.getOptionCount(CoapMsg::COAP_OPTION_BLOCK2)) {
|
||||
// The server is sending a multi-block response, make sure
|
||||
// it's sending the response block we're expecting.
|
||||
|
||||
uint32_t respBlockNum = msg.getBlock2Num();
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Block2 opt recv");
|
||||
|
||||
if (respBlockNum > m_rxBlockNum) {
|
||||
// It sent a newer block than the one we're expecting,
|
||||
// (i.e. we've somehow missed a block)
|
||||
// that's an error.
|
||||
m_lastResult = ERROR_RECEIVING_RESPONSE;
|
||||
if (msg.getType() == CoapMsg::COAP_CONFIRMABLE) {
|
||||
m_messageLayer.rejectMsg(msg);
|
||||
}
|
||||
resetChoreo();
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Received block out of order");
|
||||
break;
|
||||
|
||||
} else if (respBlockNum < m_rxBlockNum) {
|
||||
// It resent a block we've already received,
|
||||
// (i.e. it didn't see our ACK),
|
||||
// just accept (ACK) it again.
|
||||
if (msg.getType() == CoapMsg::COAP_CONFIRMABLE) {
|
||||
m_messageLayer.acceptMsg(msg);
|
||||
}
|
||||
m_lastResult = NO_ERROR;
|
||||
sendBlockRequest(m_messageID, m_rxBlockNum);
|
||||
m_state = STATE_RESPONSE_STARTED;
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Received previous block");
|
||||
|
||||
|
||||
} else {
|
||||
// Server sent the next block we are expecting.
|
||||
// Accept it and add the payload to our buffer.
|
||||
bool block2More = msg.getBlock2More();
|
||||
m_respHttpCode = msg.getHTTPStatus();
|
||||
m_lastResult = saveResponse(msg.getPayload(), msg.getPayloadLen());
|
||||
if (msg.getType() == CoapMsg::COAP_CONFIRMABLE) {
|
||||
m_rxBlockNum = respBlockNum;
|
||||
m_messageLayer.acceptMsg(msg);
|
||||
}
|
||||
if (block2More) {
|
||||
m_rxBlockNum++;
|
||||
m_messageID++;
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Request next block2 msg");
|
||||
sendBlockRequest(m_messageID, m_rxBlockNum);
|
||||
m_state = STATE_RESPONSE_STARTED;
|
||||
} else {
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Final block2 msg recv");
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Response complete");
|
||||
m_state = STATE_RESPONSE_READY;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// There's no Block2 option, so is either
|
||||
// the one and only block in the response
|
||||
// or an empty ack.
|
||||
|
||||
// check if empty to handle final ack. If empty
|
||||
// wait for CON with matching token and then
|
||||
// request other blocks of response
|
||||
|
||||
if (msg.getCode() == CoapMsg::COAP_EMPTY) {
|
||||
m_rrLayer.setState(CoapRRLayer::STATE_WAITING);
|
||||
m_messageLayer.setState(CoapMessageLayer::STATE_WAITING_FOR_CON);
|
||||
m_state = STATE_WAITING_FOR_RESPONSE;
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Empty ACK received, waiting for response");
|
||||
|
||||
} else {
|
||||
m_respHttpCode = msg.getHTTPStatus();
|
||||
m_lastResult = saveResponse(msg.getPayload(), msg.getPayloadLen());
|
||||
m_messageLayer.acceptMsg(msg);
|
||||
m_state = STATE_RESPONSE_READY;
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Response complete");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Does it have a Block2 option?
|
||||
|
||||
uint32_t responseBlockNum = msg.getBlock2Num();
|
||||
if (responseBlockNum == m_rxBlockNum && msg.getOptionCount(CoapMsg::COAP_OPTION_BLOCK2)) {
|
||||
adjustRequestBlockSize(msg);
|
||||
m_rxBlockNum++;
|
||||
if (0 == m_rxBlockNum) {
|
||||
clearData();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CoapRRLayer::ERROR_RECEIVING_RESPONSE:
|
||||
m_lastResult = ERROR_REQUEST_FAILED;
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Error receiving response");
|
||||
break;
|
||||
|
||||
case CoapRRLayer::RST_RECEIVED:
|
||||
m_lastResult = ERROR_REQUEST_FAILED;
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("RST received");
|
||||
break;
|
||||
|
||||
default:
|
||||
// Anything else indicates a failure of some sort. Check
|
||||
// the messageLayer lastResult for specifics.
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Request failed");
|
||||
m_lastResult = ERROR_REQUEST_FAILED;
|
||||
m_state = STATE_IDLE;
|
||||
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return m_lastResult;
|
||||
}
|
||||
|
||||
void TembooCoAPClient::cancelWait() {
|
||||
if (STATE_WAITING_FOR_RESPONSE == m_state) {
|
||||
m_messageLayer.cancelReliableSend();
|
||||
m_dataLen = 0;
|
||||
m_state = STATE_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool TembooCoAPClient::moreBlocksToSend() {
|
||||
uint16_t payloadLength = (m_dataLen - m_txByteCount) < m_blockSize ? (m_dataLen - m_txByteCount) : m_blockSize;
|
||||
return ((m_txByteCount + payloadLength) < m_dataLen);
|
||||
}
|
||||
|
||||
TembooCoAPClient::Result TembooCoAPClient::sendChoreoRequest() {
|
||||
uint16_t payloadLength = 0;
|
||||
|
||||
generateToken();
|
||||
|
||||
payloadLength = (m_dataLen - m_txByteCount) < m_blockSize ? (m_dataLen - m_txByteCount) : m_blockSize;
|
||||
m_txBlockNum = m_txByteCount/m_blockSize;
|
||||
bool moreBlocks = (m_txByteCount + payloadLength) < m_dataLen;
|
||||
m_lastError = sendBlock(m_messageID, &m_dataBuffer[m_txIndex], payloadLength, m_txBlockNum, moreBlocks);
|
||||
m_messageID++;
|
||||
if (TembooCoAPClient::NO_ERROR == m_lastError) {
|
||||
m_state = STATE_SEND_REQUEST;
|
||||
m_txIndex += payloadLength;
|
||||
m_txByteCount += payloadLength;
|
||||
} else {
|
||||
m_lastError = ERROR_SENDING_MSG;
|
||||
m_state = STATE_ERROR;
|
||||
}
|
||||
|
||||
|
||||
return m_lastError;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
TembooCoAPChoreo::TembooCoAPChoreo(TembooCoAPClient& client) :
|
||||
m_client(client),
|
||||
m_accountName(NULL),
|
||||
m_appKeyName(NULL),
|
||||
m_appKeyValue(NULL),
|
||||
m_path(NULL),
|
||||
m_requestId(0),
|
||||
m_availableChars(0),
|
||||
m_nextChar(NULL),
|
||||
m_nextState(END)
|
||||
{
|
||||
}
|
||||
|
||||
TembooCoAPChoreo::~TembooCoAPChoreo() {
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setAccountName(const String& accountName) {
|
||||
m_accountName = accountName.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setAccountName(const char* accountName) {
|
||||
m_accountName = accountName;
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setAppKeyName(const String& appKeyName) {
|
||||
m_appKeyName = appKeyName.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setAppKeyName(const char* appKeyName) {
|
||||
m_appKeyName = appKeyName;
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setAppKey(const String& appKeyValue) {
|
||||
m_appKeyValue = appKeyValue.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setAppKey(const char* appKeyValue) {
|
||||
m_appKeyValue = appKeyValue;
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setChoreo(const String& path) {
|
||||
m_path = path.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setChoreo(const char* path) {
|
||||
m_path = path;
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setSavedInputs(const String& savedInputsName) {
|
||||
m_preset.put(savedInputsName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setSavedInputs(const char* savedInputsName) {
|
||||
m_preset.put(savedInputsName);
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setCredential(const String& credentialName) {
|
||||
m_preset.put(credentialName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setCredential(const char* credentialName) {
|
||||
m_preset.put(credentialName);
|
||||
}
|
||||
|
||||
void TembooCoAPChoreo::setProfile(const String& profileName) {
|
||||
m_preset.put(profileName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setProfile(const char* profileName) {
|
||||
m_preset.put(profileName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addInput(const String& inputName, const String& inputValue) {
|
||||
m_inputs.put(inputName.c_str(), inputValue.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addInput(const char* inputName, const char* inputValue) {
|
||||
m_inputs.put(inputName, inputValue);
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addInput(const char* inputName, const String& inputValue) {
|
||||
m_inputs.put(inputName, inputValue.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addInput(const String& inputName, const char* inputValue) {
|
||||
m_inputs.put(inputName.c_str(), inputValue);
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addOutputFilter(const char* outputName, const char* filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName, filterPath, variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addOutputFilter(const String& outputName, const char* filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath, variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addOutputFilter(const char* outputName, const String& filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName, filterPath.c_str(), variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addOutputFilter(const String& outputName, const String& filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath.c_str(), variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addOutputFilter(const char* outputName, const char* filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName, filterPath, variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addOutputFilter(const String& outputName, const char* filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath, variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addOutputFilter(const char* outputName, const String& filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName, filterPath.c_str(), variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addOutputFilter(const String& outputName, const String& filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath.c_str(), variableName.c_str());
|
||||
}
|
||||
|
||||
int TembooCoAPChoreo::waitForResponse(TembooTimer& timer) {
|
||||
|
||||
int rc = SUCCESS;
|
||||
while (m_client.getState() == TembooCoAPClient::STATE_RESPONSE_STARTED || m_client.getState() == TembooCoAPClient::STATE_WAITING_FOR_RESPONSE) {
|
||||
if (timer.expired()) {
|
||||
TEMBOO_TRACELN("ERROR: Choreo timeout");
|
||||
rc = TEMBOO_ERROR_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
m_client.loop();
|
||||
// While the buffer may be full, we need to receive all of the data
|
||||
// from the gateway even though we discard it. We still return
|
||||
// the buffer error code, but the user is still able to see what
|
||||
// data was able to fit in the current buffer
|
||||
if (m_client.getMessageState() != TembooCoAPClient::NO_ERROR && m_client.getMessageState() != TembooCoAPClient::ERROR_BUFFER_FULL) {
|
||||
rc = FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int TembooCoAPChoreo::run(uint16_t timeoutSecs) {
|
||||
m_nextChar = NULL;
|
||||
|
||||
if (IS_EMPTY(m_accountName)) {
|
||||
return TEMBOO_ERROR_ACCOUNT_MISSING;
|
||||
}
|
||||
|
||||
if (IS_EMPTY(m_path)) {
|
||||
return TEMBOO_ERROR_CHOREO_MISSING;
|
||||
}
|
||||
|
||||
if (IS_EMPTY(m_appKeyName)) {
|
||||
return TEMBOO_ERROR_APPKEY_NAME_MISSING;
|
||||
}
|
||||
|
||||
if (IS_EMPTY(m_appKeyValue)) {
|
||||
return TEMBOO_ERROR_APPKEY_MISSING;
|
||||
}
|
||||
int rc = 0;
|
||||
|
||||
TembooTimer timer(timeoutSecs * 1000L);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
|
||||
m_client.resetChoreo();
|
||||
|
||||
TembooCoAPSession session(m_client);
|
||||
m_requestId = s_nextRequestId++;
|
||||
|
||||
m_respData = NULL;
|
||||
m_availableChars = 0;
|
||||
m_nextState = START;
|
||||
uint16toa(0 , m_httpCodeStr);
|
||||
|
||||
m_client.getNextMessageID();
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Sending request");
|
||||
rc = session.executeChoreo(m_requestId, m_accountName, m_appKeyName, m_appKeyValue, m_path, m_inputs, m_expressions, m_sensors, m_outputs, m_preset, m_deviceType, m_deviceName);
|
||||
if (SUCCESS != rc) {
|
||||
goto ErrorExit;
|
||||
}
|
||||
|
||||
// finish sending Choreo request
|
||||
while (m_client.getState() == TembooCoAPClient::STATE_SEND_REQUEST) {
|
||||
if(m_client.loop() == TembooCoAPClient::ERROR_REQUEST_FAILED) {
|
||||
rc = m_client.getMessageState();
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Choreo request failed");
|
||||
goto ErrorExit;
|
||||
}
|
||||
}
|
||||
|
||||
// choreo request complete, wait for CON from gateway
|
||||
// and then request the rest of the response
|
||||
|
||||
rc = waitForResponse(timer);
|
||||
if (SUCCESS != rc){
|
||||
rc = m_client.getMessageState();
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Waiting for response failed");
|
||||
goto ErrorExit;
|
||||
} else {
|
||||
|
||||
m_respData = (char*)m_client.getPacketBuffer();
|
||||
uint16_t httpCode = m_client.getRespHttpCode();
|
||||
if (httpCode >= 700) {
|
||||
httpCode = 0;
|
||||
}
|
||||
|
||||
uint16toa(httpCode, m_httpCodeStr);
|
||||
m_availableChars = strlen(m_respData) + strlen(m_httpCodeStr) + strlen(HTTP_CODE_PREFIX) + strlen(HTTP_CODE_SUFFIX);
|
||||
|
||||
m_nextChar = HTTP_CODE_PREFIX;
|
||||
|
||||
//Unauthroized, need to update the time
|
||||
if (httpCode == 401 && i == 0) {
|
||||
find(HEADER_TIME);
|
||||
TembooCoAPSession::setTime((unsigned long)this->parseInt());
|
||||
} else {
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACE(m_availableChars);
|
||||
TEMBOO_TRACELN(" CHARS");
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Response buffer data:");
|
||||
TEMBOO_TRACELN(m_respData);
|
||||
|
||||
rc = m_client.getMessageState();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ErrorExit:
|
||||
if (SUCCESS != rc) {
|
||||
TEMBOO_TRACE(" ERROR:");
|
||||
TEMBOO_TRACELN(rc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int TembooCoAPChoreo::available() {
|
||||
return m_availableChars;
|
||||
}
|
||||
|
||||
|
||||
int TembooCoAPChoreo::peek() {
|
||||
if (m_availableChars > 0) {
|
||||
return (int)*m_nextChar;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int TembooCoAPChoreo::read() {
|
||||
|
||||
if (m_availableChars > 0) {
|
||||
int c = 0;
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
m_nextChar = HTTP_CODE_PREFIX;
|
||||
c = (int)(*m_nextChar++);
|
||||
m_nextState = HTTP_CODE_PRE;
|
||||
break;
|
||||
|
||||
case HTTP_CODE_PRE:
|
||||
c = (int)(*m_nextChar++);
|
||||
if ('\0' == *m_nextChar) {
|
||||
m_nextState = HTTP_CODE_VALUE;
|
||||
m_nextChar = m_httpCodeStr;
|
||||
}
|
||||
break;
|
||||
|
||||
case HTTP_CODE_VALUE:
|
||||
c = (int)(*m_nextChar++);
|
||||
if (*m_nextChar == '\0') {
|
||||
m_nextState = HTTP_CODE_SUF;
|
||||
m_nextChar = HTTP_CODE_SUFFIX;
|
||||
}
|
||||
break;
|
||||
|
||||
case HTTP_CODE_SUF:
|
||||
c = (int)(*m_nextChar++);
|
||||
if ('\0' == *m_nextChar) {
|
||||
m_nextState = RESP_DATA;
|
||||
m_nextChar = m_respData;
|
||||
}
|
||||
break;
|
||||
|
||||
case RESP_DATA:
|
||||
c = (int)(*m_nextChar++);
|
||||
if ('\0' == *m_nextChar || m_availableChars <= 0) {
|
||||
m_nextState = END;
|
||||
}
|
||||
break;
|
||||
|
||||
case END:
|
||||
default:
|
||||
c = -1;
|
||||
}
|
||||
if (m_availableChars > 0) {
|
||||
m_availableChars--;
|
||||
}
|
||||
return c;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t TembooCoAPChoreo::write(uint8_t __attribute__ ((unused)) data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::flush() {
|
||||
m_nextChar = NULL;
|
||||
m_nextState = END;
|
||||
m_availableChars = 0;
|
||||
}
|
||||
|
||||
317
arduino/libraries/Temboo/src/TembooCoAPEdgeDevice.h
Normal file
317
arduino/libraries/Temboo/src/TembooCoAPEdgeDevice.h
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOCOAP_H_
|
||||
#define TEMBOOCOAP_H_
|
||||
|
||||
#ifndef TEMBOO_LIBRARY_VERSION
|
||||
#define TEMBOO_LIBRARY_VERSION 2
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// BEGIN ARDUINO NON-YUN SUPPORT
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "utility/TembooTimer.h"
|
||||
#include "utility/TembooTags.h"
|
||||
|
||||
#include "utility/TembooCoAPIPStack.h"
|
||||
#include "utility/ChoreoInputSet.h"
|
||||
#include "utility/ChoreoInputExpressionSet.h"
|
||||
#include "utility/ChoreoSensorInputSet.h"
|
||||
#include "utility/ChoreoOutputSet.h"
|
||||
#include "utility/ChoreoPreset.h"
|
||||
#include "utility/ChoreoDevice.h"
|
||||
#include "utility/CoapMsg.h"
|
||||
#include "utility/CoapMessageLayer.h"
|
||||
#include "utility/CoapRRLayer.h"
|
||||
|
||||
|
||||
#define IS_EMPTY(s) (NULL == s || '\0' == *s)
|
||||
#define DEFAULT_CHOREO_TIMEOUT 900
|
||||
|
||||
class TembooCoAPChoreo;
|
||||
|
||||
class TembooCoAPClient {
|
||||
public:
|
||||
TembooCoAPClient(TembooCoAPIPStack& ipStack, IPAddress gatewayAddress, uint16_t gatewayPort = DEFAULT_COAP_PORT);
|
||||
virtual ~TembooCoAPClient();
|
||||
void begin(long seed);
|
||||
|
||||
enum Result {
|
||||
NO_ERROR = 0,
|
||||
ERROR_REQUEST_FAILED,
|
||||
ERROR_BAD_RESPONSE,
|
||||
ERROR_MSG_TOKEN,
|
||||
ERROR_MSG_OPTION,
|
||||
ERROR_MSG_PAYLOAD,
|
||||
ERROR_SENDING_MSG,
|
||||
ERROR_RECEIVING_MSG,
|
||||
ERROR_BUFFER_FULL,
|
||||
ERROR_INVALID_MSG,
|
||||
ERROR_RECEIVING_RESPONSE
|
||||
};
|
||||
|
||||
enum State {
|
||||
STATE_IDLE,
|
||||
STATE_TRANSMIT,
|
||||
STATE_WAITING_FOR_RESPONSE,
|
||||
STATE_RESPONSE_STARTED,
|
||||
STATE_RESPONSE_READY,
|
||||
STATE_NO_RESPONSE,
|
||||
STATE_SEND_REQUEST,
|
||||
STATE_ERROR
|
||||
};
|
||||
|
||||
Result write(uint8_t value);
|
||||
Result write(uint8_t* value, uint16_t len);
|
||||
void clearData() {m_dataLen = 0;}
|
||||
Result loop();
|
||||
Result sendChoreoRequest();
|
||||
uint8_t* getPacketBuffer() {return m_respBuffer;}
|
||||
int32_t getPacketBufferSize() {return m_respLen;}
|
||||
int32_t getPacketLength() {return 1000;}
|
||||
int16_t getRespHttpCode() {return m_respHttpCode;}
|
||||
State getState() {return m_state;}
|
||||
Result sendBlockRequest(uint16_t msgID, uint32_t blockNum);
|
||||
void resetChoreo();
|
||||
int getMessageState() {return m_lastResult;}
|
||||
Result requestTime(uint16_t msgID);
|
||||
|
||||
protected:
|
||||
static const char URI_PATH[];
|
||||
Result m_lastResult;
|
||||
bool moreBlocksToSend();
|
||||
|
||||
// MAX_BLOCK_SIZE *MUST* be one of the standard CoAP block sizes
|
||||
// This size should be set with consideration to any network or
|
||||
// hardware limitations on UDP packet size.
|
||||
// (16, 32, 64, 128, 256, 512, or 1024).
|
||||
static const int MAX_BLOCK_SIZE = 64;
|
||||
|
||||
// MAX_PACKET_SIZE should be at least big enough to hold:
|
||||
// for outgoing requests:
|
||||
// 4 header bytes
|
||||
// 6 token bytes (in our case, Spec says tokens can be up to 8 bytes).
|
||||
// 1 byte for the URI PATH option (I *think*)
|
||||
// 4 strlen(URI_PATH) bytes for the URI PATH option value.
|
||||
// 3 bytes for a block1 option
|
||||
// 3 bytes for a block1 option value
|
||||
// 1 bytes for a block2 option (early response size negotiation)
|
||||
// 1 bytes for a block2 option value
|
||||
// ? 5 bytes for a size1 or size2 option
|
||||
// ? 4 bytes for a sizeX option value
|
||||
// 1 byte for the FF payload marker
|
||||
// MAX_BLOCK_SIZE for the payload
|
||||
//
|
||||
// or 24 + MAX_BLOCK_SIZE
|
||||
//
|
||||
// HOWEVER... we need to consider the possibility that the server may
|
||||
// use more options and thus send more bytes. So we should add as much
|
||||
// extra space as we can reasonably afford so as to avoid buffer overflows.
|
||||
|
||||
static const size_t MAX_PACKET_SIZE = 90;
|
||||
|
||||
|
||||
static const size_t MAX_DATA_SIZE = 1000;
|
||||
|
||||
static const uint16_t DEFAULT_COAP_PORT = 5683;
|
||||
|
||||
CoapMessageLayer m_messageLayer;
|
||||
CoapRRLayer m_rrLayer;
|
||||
IPAddress m_gatewayAddress;
|
||||
uint16_t m_gatewayPort;
|
||||
uint16_t m_messageID;
|
||||
State m_state;
|
||||
uint16_t m_blockSize;
|
||||
Result m_lastError;
|
||||
|
||||
uint16_t m_dataLen;
|
||||
uint8_t m_dataBuffer[MAX_DATA_SIZE];
|
||||
uint8_t m_respBuffer[MAX_DATA_SIZE];
|
||||
|
||||
char m_token[9];
|
||||
uint8_t m_txBuffer[MAX_PACKET_SIZE];
|
||||
uint8_t m_rxBuffer[MAX_PACKET_SIZE];
|
||||
uint32_t m_rxBlockNum;
|
||||
int32_t m_txIndex;
|
||||
int32_t m_txByteCount;
|
||||
int32_t m_respLen;
|
||||
uint32_t m_txBlockNum;
|
||||
int16_t m_respHttpCode;
|
||||
|
||||
Result generateToken();
|
||||
uint16_t getNextMessageID();
|
||||
|
||||
Result sendBlock(uint16_t msgID, uint8_t* payload, size_t len, uint32_t blockNum, bool moreBlocks);
|
||||
void adjustRequestBlockSize(CoapMsg& msg);
|
||||
void cancelWait();
|
||||
|
||||
int send(CoapMsg* msg);
|
||||
int sendResetMsg(CoapMsg& msg);
|
||||
int sendEmptyAckMsg(CoapMsg& msg);
|
||||
int sendBlock2AckMsg(CoapMsg& msg);
|
||||
bool isMsgResponse(CoapMsg& msg);
|
||||
void handleBlockAck(CoapMsg& msg);
|
||||
void handleBlock1InResponse(CoapMsg& msg);
|
||||
Result saveResponse(uint8_t* values, uint16_t len);
|
||||
|
||||
friend class TembooCoAPChoreo;
|
||||
};
|
||||
|
||||
class TembooCoAPChoreo : public Stream {
|
||||
public:
|
||||
|
||||
// Constructor.
|
||||
// client - an instance of a TembooCoAPClient.
|
||||
// Used to communicate with a Temboo CoAP Gateway.
|
||||
TembooCoAPChoreo(TembooCoAPClient& client);
|
||||
~TembooCoAPChoreo();
|
||||
|
||||
// Does nothing. Just for source compatibility with Yun code.
|
||||
void begin() {;};
|
||||
|
||||
// Sets the account name to use when communicating with Temboo.
|
||||
// (required)
|
||||
void setAccountName(const String& accountName);
|
||||
void setAccountName(const char* accountName);
|
||||
|
||||
// Sets the application key name to use with choreo execution requests.
|
||||
// (required)
|
||||
void setAppKeyName(const String& appKeyName);
|
||||
void setAppKeyName(const char* appKeyName);
|
||||
|
||||
// Sets the application key value to use with choreo execution requests
|
||||
// (required)
|
||||
void setAppKey(const String& appKey);
|
||||
void setAppKey(const char* appKey);
|
||||
|
||||
// sets the name of the choreo to be executed.
|
||||
// (required)
|
||||
void setChoreo(const String& choreoPath);
|
||||
void setChoreo(const char* choreoPath);
|
||||
|
||||
|
||||
// sets the name of the saved inputs to use when executing the choreo
|
||||
// (optional)
|
||||
void setSavedInputs(const String& savedInputsName);
|
||||
void setSavedInputs(const char* savedInputsName);
|
||||
|
||||
void setCredential(const String& credentialName);
|
||||
void setCredential(const char* credentialName);
|
||||
|
||||
void setProfile(const String& profileName);
|
||||
void setProfile(const char* profileName);
|
||||
|
||||
// sets an input to be used when executing a choreo.
|
||||
// (optional or required, depending on the choreo being executed.)
|
||||
void addInput(const String& inputName, const String& inputValue);
|
||||
void addInput(const char* inputName, const char* inputValue);
|
||||
void addInput(const char* inputName, const String& inputValue);
|
||||
void addInput(const String& inputName, const char* inputValue);
|
||||
|
||||
// sets an output filter to be used to process the choreo output
|
||||
// (optional)
|
||||
void addOutputFilter(const char* filterName, const char* filterPath, const char* variableName);
|
||||
void addOutputFilter(const String& filterName, const char* filterPath, const char* variableName);
|
||||
void addOutputFilter(const char* filterName, const String& filterPath, const char* variableName);
|
||||
void addOutputFilter(const String& filterName, const String& filterPath, const char* variableName);
|
||||
void addOutputFilter(const char* filterName, const char* filterPath, const String& variableName);
|
||||
void addOutputFilter(const String& filterName, const char* filterPath, const String& variableName);
|
||||
void addOutputFilter(const char* filterName, const String& filterPath, const String& variableName);
|
||||
void addOutputFilter(const String& filterName, const String& filterPath, const String& variableName);
|
||||
|
||||
// run the choreo using the current input info
|
||||
int run(uint16_t timeoutSecs = DEFAULT_CHOREO_TIMEOUT);
|
||||
|
||||
char* getResponseData() {return m_respData;}
|
||||
char* getHTTPResponseCode() {return m_httpCodeStr;}
|
||||
|
||||
|
||||
// Stream interface
|
||||
void close() {};
|
||||
int available();
|
||||
int read();
|
||||
int peek();
|
||||
void flush();
|
||||
|
||||
//Print interface
|
||||
size_t write(uint8_t data);
|
||||
|
||||
enum Error {
|
||||
SUCCESS = 0,
|
||||
FAILURE,
|
||||
TEMBOO_ERROR_ACCOUNT_MISSING = 201,
|
||||
TEMBOO_ERROR_CHOREO_MISSING = 203,
|
||||
TEMBOO_ERROR_APPKEY_NAME_MISSING = 205,
|
||||
TEMBOO_ERROR_APPKEY_MISSING = 207,
|
||||
TEMBOO_ERROR_HTTP_ERROR = 223,
|
||||
TEMBOO_ERROR_TIMEOUT = 225,
|
||||
TEMBOO_ERROR_MEMORY = 900,
|
||||
TEMBOO_ERROR_TCPIP_CONNECT_FAIL = 901,
|
||||
TEMBOO_ERROR_NO_RESPONSE = 0xFFFF
|
||||
};
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
static const size_t MAX_RESPONSE_SIZE = 900;
|
||||
|
||||
TembooCoAPClient& m_client;
|
||||
const char* m_accountName;
|
||||
const char* m_appKeyName;
|
||||
const char* m_appKeyValue;
|
||||
const char* m_path;
|
||||
|
||||
ChoreoInputSet m_inputs;
|
||||
ChoreoInputExpressionSet m_expressions;
|
||||
ChoreoSensorInputSet m_sensors;
|
||||
ChoreoOutputSet m_outputs;
|
||||
ChoreoPreset m_preset;
|
||||
ChoreoDevice m_deviceType;
|
||||
ChoreoDevice m_deviceName;
|
||||
|
||||
char m_httpCodeStr[4];
|
||||
char* m_respData;
|
||||
|
||||
uint16_t m_requestId;
|
||||
static uint16_t s_nextRequestId;
|
||||
|
||||
// variables for the stream interface
|
||||
size_t m_availableChars;
|
||||
const char* m_nextChar;
|
||||
enum State {START,
|
||||
HTTP_CODE_PRE,
|
||||
HTTP_CODE_VALUE,
|
||||
HTTP_CODE_SUF,
|
||||
RESP_DATA,
|
||||
END};
|
||||
State m_nextState;
|
||||
|
||||
protected:
|
||||
int waitForResponse(TembooTimer& timer);
|
||||
uint16_t getRequestId() {return m_requestId;}
|
||||
};
|
||||
|
||||
|
||||
#endif //TEMBOO_H_
|
||||
861
arduino/libraries/Temboo/src/TembooMQTTEdgeDevice.cpp
Normal file
861
arduino/libraries/Temboo/src/TembooMQTTEdgeDevice.cpp
Normal file
@@ -0,0 +1,861 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo MQTT Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// BEGIN ARDUINO NON-YUN SUPPORT
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#include "utility/TembooGlobal.h"
|
||||
#include "utility/TembooMQTTSession.h"
|
||||
#include "utility/MQTTPacket.h"
|
||||
|
||||
#include "TembooMQTTEdgeDevice.h"
|
||||
|
||||
#ifndef UINT16_MAX
|
||||
#define UINT16_MAX (0xFFFF)
|
||||
#endif
|
||||
|
||||
static const char HTTP_CODE_PREFIX[] PROGMEM = "HTTP_CODE\x0A\x1F";
|
||||
static const char HTTP_CODE_SUFFIX[] PROGMEM = "\x0A\x1E";
|
||||
|
||||
static const char REQUEST_TOPIC_PREFIX[] PROGMEM = "/temboo/req/";
|
||||
static const char REQUEST_ACK_TOPIC_PREFIX[] PROGMEM = "/temboo/ack/";
|
||||
static const char RESPONSE_TOPIC_PREFIX[] PROGMEM = "/temboo/resp/";
|
||||
static const char RESPONSE_DATA_TOPIC_PREFIX[] PROGMEM = "/temboo/resp-data/";
|
||||
|
||||
// TIME_TOPIC must NOT be PROGMEM
|
||||
static const char TIME_TOPIC[] = "/temboo/time";
|
||||
|
||||
static TembooMQTTChoreo* volatile g_currentChoreo = NULL;
|
||||
uint16_t TembooMQTTChoreo::s_nextRequestId = 0;
|
||||
|
||||
void handleDataMessage(MQTT::MessageData& md) {
|
||||
|
||||
TEMBOO_TRACE("data: ");
|
||||
|
||||
MQTT::Message& msg = md.message;
|
||||
|
||||
char* c;
|
||||
unsigned int count;
|
||||
for (c = (char*)msg.payload, count = 0; *c != '\0' && isdigit(*c) && count < msg.payloadlen; c++) {
|
||||
count++;
|
||||
if (count > 5) {
|
||||
TEMBOO_TRACELN(" long");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
TEMBOO_TRACELN(" short");
|
||||
return;
|
||||
}
|
||||
|
||||
if (TAG_VALUE_SEPARATOR != *c) {
|
||||
TEMBOO_TRACELN(" bad msg");
|
||||
return;
|
||||
}
|
||||
|
||||
// Replace the : with \0 so we can use strtoul.
|
||||
*c = '\0';
|
||||
|
||||
unsigned long requestId = strtoul((char*)msg.payload, NULL, 10);
|
||||
if (UINT16_MAX < requestId) {
|
||||
TEMBOO_TRACELN(" bad id");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((NULL == g_currentChoreo) || (g_currentChoreo->getRequestId() != (uint16_t)requestId)) {
|
||||
TEMBOO_TRACE(" stale id: ");
|
||||
TEMBOO_TRACELN(requestId);
|
||||
return;
|
||||
}
|
||||
|
||||
g_currentChoreo->setResponseData(c + 1, msg.payloadlen - strlen((char*)msg.payload) - 1);
|
||||
TEMBOO_TRACELN(" ok");
|
||||
|
||||
}
|
||||
|
||||
bool validateUint16PairMessage(const char* msg) {
|
||||
const char* c;
|
||||
int count = 0;
|
||||
for (c = msg; *c != '\0' && isdigit(*c); c++) {
|
||||
count++;
|
||||
if (count > 5)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TAG_VALUE_SEPARATOR != *c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
c++;
|
||||
for (count = 0; *c != '\0' && isdigit(*c); c++) {
|
||||
count++;
|
||||
if (count > 5) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (count == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void handleResponseMessage(MQTT::MessageData& md) {
|
||||
|
||||
TEMBOO_TRACE("resp: ");
|
||||
|
||||
MQTT::Message& msg = md.message;
|
||||
|
||||
// Expected max length is 9 (for 65535:599)
|
||||
if (msg.payloadlen > 9) {
|
||||
TEMBOO_TRACELN("long");
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.payloadlen < 3) {
|
||||
TEMBOO_TRACELN("short");
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy the payload and nul terminate it;
|
||||
char respStr[10];
|
||||
memcpy(respStr, msg.payload, msg.payloadlen);
|
||||
respStr[msg.payloadlen] = '\0';
|
||||
|
||||
TEMBOO_TRACE(respStr);
|
||||
|
||||
if (!validateUint16PairMessage(respStr)) {
|
||||
TEMBOO_TRACELN(" bad msg");
|
||||
return;
|
||||
}
|
||||
|
||||
char* next;
|
||||
unsigned long respCode = 0;
|
||||
unsigned long requestId = strtoul(respStr, &next, 10);
|
||||
|
||||
// validate only checks that the request ID
|
||||
// has at least 1 but no more than 6 digits.
|
||||
// so we have to check the actual value here.
|
||||
if (UINT16_MAX < requestId) {
|
||||
TEMBOO_TRACELN(" bad id");
|
||||
return;
|
||||
}
|
||||
|
||||
// If the request ID in the message doesn't match the
|
||||
// current request ID, then it's a stale ack.
|
||||
if ((NULL == g_currentChoreo) || (g_currentChoreo->getRequestId() != (uint16_t)requestId)) {
|
||||
TEMBOO_TRACE(" stale id: ");
|
||||
TEMBOO_TRACELN(respStr);
|
||||
return;
|
||||
}
|
||||
|
||||
next++;
|
||||
respCode = strtoul(next, NULL, 10);
|
||||
|
||||
// Validate only checks that the ack code
|
||||
// has at least 1 but no more than 6 digits,
|
||||
// so we have to check the actual value here.
|
||||
if (1000 <= respCode) {
|
||||
TEMBOO_TRACELN(" bad code");
|
||||
return;
|
||||
}
|
||||
|
||||
// FINALLY, everything's OK.
|
||||
// pass the value to the waiting choreo.
|
||||
g_currentChoreo->setHTTPResponseCode(next);
|
||||
TEMBOO_TRACELN(" ok");
|
||||
|
||||
}
|
||||
|
||||
|
||||
void handleTimeMessage(MQTT::MessageData& md) {
|
||||
|
||||
TEMBOO_TRACE("time: ");
|
||||
|
||||
MQTT::Message& msg = md.message;
|
||||
|
||||
// Time messages should be <= 10 characters long.
|
||||
if (msg.payloadlen > 10) {
|
||||
TEMBOO_TRACELN("long");
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.payloadlen == 0) {
|
||||
TEMBOO_TRACELN("short");
|
||||
return;
|
||||
}
|
||||
|
||||
// Payload should consist only of digits (0 - 9)
|
||||
for (unsigned int i = 0; i < msg.payloadlen; i++) {
|
||||
if (! isdigit(((char*)msg.payload)[i])) {
|
||||
TEMBOO_TRACELN("!digit");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
char timeStr[11];
|
||||
memcpy(timeStr, msg.payload, msg.payloadlen);
|
||||
timeStr[msg.payloadlen] = '\0';
|
||||
|
||||
TEMBOO_TRACE(timeStr);
|
||||
|
||||
uint32_t t = strtoul(timeStr, NULL, 10);
|
||||
TembooMQTTSession::setTime(t);
|
||||
|
||||
TEMBOO_TRACELN(" ok");
|
||||
}
|
||||
|
||||
void handleAckMessage(MQTT::MessageData& md) {
|
||||
|
||||
TEMBOO_TRACE("ack: ");
|
||||
|
||||
MQTT::Message& msg = md.message;
|
||||
|
||||
// Expected max length is 11 (for 65535:65535)
|
||||
if (msg.payloadlen > 11) {
|
||||
TEMBOO_TRACELN("long");
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.payloadlen == 0) {
|
||||
TEMBOO_TRACELN("short");
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy the payload and nul terminate it;
|
||||
char ackStr[12];
|
||||
memcpy(ackStr, msg.payload, msg.payloadlen);
|
||||
ackStr[msg.payloadlen] = '\0';
|
||||
|
||||
TEMBOO_TRACE(ackStr);
|
||||
|
||||
if (!validateUint16PairMessage(ackStr)) {
|
||||
TEMBOO_TRACELN(" bad msg");
|
||||
return;
|
||||
}
|
||||
|
||||
char* next;
|
||||
unsigned long ackCode = TEMBOO_ERROR_FAILURE;
|
||||
unsigned long requestId = strtoul(ackStr, &next, 10);
|
||||
|
||||
// validate only checks that the request ID
|
||||
// has at least 1 but no more than 6 digits.
|
||||
// so we have to check the actual value here.
|
||||
if (UINT16_MAX < requestId) {
|
||||
TEMBOO_TRACELN(" bad id");
|
||||
return;
|
||||
}
|
||||
|
||||
// If the request ID in the message doesn't match the
|
||||
// current request ID, then it's a stale ack.
|
||||
if ((NULL == g_currentChoreo) || (g_currentChoreo->getRequestId() != (uint16_t)requestId)) {
|
||||
TEMBOO_TRACE(" stale id: ");
|
||||
TEMBOO_TRACELN(ackStr);
|
||||
return;
|
||||
}
|
||||
|
||||
next++;
|
||||
ackCode = strtoul(next, NULL, 10);
|
||||
|
||||
// Validate only checks that the ack code
|
||||
// has at least 1 but no more than 6 digits,
|
||||
// so we have to check the actual value here.
|
||||
if (UINT16_MAX < ackCode) {
|
||||
TEMBOO_TRACELN(" bad code");
|
||||
return;
|
||||
}
|
||||
|
||||
// FINALLY, everything's OK.
|
||||
// pass the value to the waiting choreo.
|
||||
g_currentChoreo->setAckCode(ackCode);
|
||||
TEMBOO_TRACELN(" ok");
|
||||
|
||||
}
|
||||
|
||||
|
||||
TembooMQTTClient::TembooMQTTClient(TembooMQTTIPStack& ipStack, unsigned int commandTimeoutMs )
|
||||
: BaseClient (ipStack, commandTimeoutMs),
|
||||
m_ipStack(ipStack) {
|
||||
m_deviceId = NULL;
|
||||
m_requestTopic = NULL;
|
||||
m_ackTopic = NULL;
|
||||
m_responseTopic = NULL;
|
||||
m_dataTopic = NULL;
|
||||
}
|
||||
|
||||
void TembooMQTTClient::makeTopics() {
|
||||
m_requestTopic = strCatNew_P(REQUEST_TOPIC_PREFIX, m_deviceId);
|
||||
m_ackTopic = strCatNew_P(REQUEST_ACK_TOPIC_PREFIX, m_deviceId);
|
||||
m_responseTopic = strCatNew_P(RESPONSE_TOPIC_PREFIX, m_deviceId);
|
||||
m_dataTopic = strCatNew_P(RESPONSE_DATA_TOPIC_PREFIX, m_deviceId);
|
||||
}
|
||||
|
||||
int TembooMQTTClient::setDeviceId(char* id) {
|
||||
int rc = TEMBOO_ERROR_FAILURE;
|
||||
if (NULL == m_deviceId) {
|
||||
m_deviceId = new char[strlen(id) + 1];
|
||||
if (NULL != m_deviceId) {
|
||||
strcpy(m_deviceId, id);
|
||||
makeTopics();
|
||||
rc = TEMBOO_ERROR_OK;
|
||||
} else {
|
||||
rc = TEMBOO_ERROR_MEMORY;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int TembooMQTTClient::setDeviceIdFromMac(byte (&mac)[6]) {
|
||||
int rc = TEMBOO_ERROR_FAILURE;
|
||||
|
||||
// Only do something if we don't already have a deviceId
|
||||
if (NULL == m_deviceId) {
|
||||
// generate the deviceId from the MAC address.
|
||||
char macStr[13];
|
||||
for (unsigned int i = 0; i < sizeof(mac)/sizeof(*mac); i++) {
|
||||
byte m = mac[i] >> 4;
|
||||
macStr[2 * i] = m < 10 ? ('0' + m):('A' + m - 10);
|
||||
m = mac[i] & 0x0F;
|
||||
macStr[2 * i + 1] = m < 10 ? ('0' + m):('A' + m - 10);
|
||||
}
|
||||
macStr[12] = '\0';
|
||||
rc = setDeviceId(macStr);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
char* TembooMQTTClient::strCatNew_P(const char* s1, const char* s2) {
|
||||
size_t len = strlen_P(s1) + strlen(s2) + 1;
|
||||
char* result = new char[len];
|
||||
if (NULL != result) {
|
||||
strcpy_P(result, s1);
|
||||
strcat(result, s2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TembooMQTTClient::~TembooMQTTClient() {
|
||||
if (NULL != m_requestTopic) delete[] m_requestTopic;
|
||||
if (NULL != m_ackTopic) delete[] m_ackTopic;
|
||||
if (NULL != m_responseTopic) delete[] m_responseTopic;
|
||||
if (NULL != m_dataTopic) delete[] m_dataTopic;
|
||||
if (NULL != m_deviceId) delete[] m_deviceId;
|
||||
}
|
||||
|
||||
int TembooMQTTClient::connect(const char* hostname, int port) {
|
||||
int rc = TEMBOO_ERROR_FAILURE;
|
||||
|
||||
if (NULL == m_deviceId) {
|
||||
return TEMBOO_ERROR_DEVICE_ID_MISSING;
|
||||
}
|
||||
|
||||
TEMBOO_TRACE("DEVICE ID: ");
|
||||
TEMBOO_TRACELN(m_deviceId);
|
||||
|
||||
// Open a socket to the broker.
|
||||
TEMBOO_TRACE("IP: ");
|
||||
rc = m_ipStack.connect(hostname, port);
|
||||
if (1 != rc) {
|
||||
TEMBOO_TRACELN(rc);
|
||||
rc = TEMBOO_ERROR_TCPIP_CONNECT_FAIL;
|
||||
goto ErrorExit;
|
||||
} else {
|
||||
TEMBOO_TRACELN("OK");
|
||||
|
||||
// Establish an MQTT connection with the broker.
|
||||
TEMBOO_TRACE("MQ ");
|
||||
TEMBOO_TRACE(m_deviceId);
|
||||
TEMBOO_TRACE(": ");
|
||||
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
|
||||
data.MQTTVersion = 3;
|
||||
data.clientID.cstring = (char*)m_deviceId;
|
||||
rc = connect(data);
|
||||
if (MQTT::SUCCESS != rc) {
|
||||
TEMBOO_TRACELN(rc);
|
||||
rc = TEMBOO_ERROR_MQTT_CONNECT_FAIL;
|
||||
goto ErrorExit;
|
||||
}
|
||||
TEMBOO_TRACELN("OK");
|
||||
}
|
||||
|
||||
// Subscribe to the various topics we need to monitor.
|
||||
//NOTE: 'subscribe()' does NOT copy the topic strings,
|
||||
//so those strings must stay valid for the life of the connection.
|
||||
|
||||
TEMBOO_TRACE("SUB:");
|
||||
//Yes, it's slightly risky counting on MQTT::SUCCESS always being 0.
|
||||
rc = this->subscribe(m_ackTopic, MQTT::QOS1, handleAckMessage);
|
||||
rc |= this->subscribe(m_responseTopic, MQTT::QOS1, handleResponseMessage);
|
||||
rc |= this->subscribe(m_dataTopic, MQTT::QOS1, handleDataMessage);
|
||||
rc |= this->subscribe(TIME_TOPIC, MQTT::QOS1, handleTimeMessage);
|
||||
if (MQTT::SUCCESS != rc) {
|
||||
TEMBOO_TRACELN("NO");
|
||||
rc = TEMBOO_ERROR_MQTT_SUBSCRIBE_FAIL;
|
||||
goto ErrorExit;
|
||||
}
|
||||
TEMBOO_TRACELN("OK");
|
||||
|
||||
return TEMBOO_ERROR_OK;
|
||||
|
||||
ErrorExit:
|
||||
if (isConnected()) {
|
||||
disconnect();
|
||||
}
|
||||
m_ipStack.disconnect();
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool TembooMQTTClient::isConnected() {
|
||||
if (m_ipStack.isConnected()) {
|
||||
return BaseClient::isConnected();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int TembooMQTTClient::sendChoreoRequest(const char* request, size_t len) {
|
||||
MQTT::Message message;
|
||||
|
||||
message.qos = MQTT::QOS0;
|
||||
message.retained = false;
|
||||
message.dup = false;
|
||||
message.payload = (void*)request;
|
||||
message.payloadlen = len;
|
||||
int rc = this->publish(this->m_requestTopic, message);
|
||||
return rc == MQTT::SUCCESS ? TEMBOO_ERROR_OK : rc;
|
||||
}
|
||||
|
||||
TembooMQTTChoreo::TembooMQTTChoreo(TembooMQTTClient& client) :
|
||||
m_client(client),
|
||||
m_accountName(NULL),
|
||||
m_appKeyName(NULL),
|
||||
m_appKeyValue(NULL),
|
||||
m_path(NULL),
|
||||
m_haveHttpCode(false),
|
||||
m_ackCode(0),
|
||||
m_haveAckCode(false),
|
||||
m_haveData(false),
|
||||
m_requestId(0),
|
||||
m_availableChars(0),
|
||||
m_nextChar(NULL),
|
||||
m_nextState(END),
|
||||
m_packetStatus(TEMBOO_ERROR_OK)
|
||||
{
|
||||
}
|
||||
|
||||
TembooMQTTChoreo::~TembooMQTTChoreo() {
|
||||
memset(m_respData, '\0', sizeof(m_respData)/sizeof(m_respData[0]));
|
||||
}
|
||||
|
||||
void TembooMQTTChoreo::setAccountName(const String& accountName) {
|
||||
m_accountName = accountName.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setAccountName(const char* accountName) {
|
||||
m_accountName = accountName;
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setAppKeyName(const String& appKeyName) {
|
||||
m_appKeyName = appKeyName.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setAppKeyName(const char* appKeyName) {
|
||||
m_appKeyName = appKeyName;
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setAppKey(const String& appKeyValue) {
|
||||
m_appKeyValue = appKeyValue.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setAppKey(const char* appKeyValue) {
|
||||
m_appKeyValue = appKeyValue;
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setChoreo(const String& path) {
|
||||
m_path = path.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setChoreo(const char* path) {
|
||||
m_path = path;
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setSavedInputs(const String& savedInputsName) {
|
||||
m_preset.put(savedInputsName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setSavedInputs(const char* savedInputsName) {
|
||||
m_preset.put(savedInputsName);
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setCredential(const String& credentialName) {
|
||||
m_preset.put(credentialName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setCredential(const char* credentialName) {
|
||||
m_preset.put(credentialName);
|
||||
}
|
||||
|
||||
void TembooMQTTChoreo::setProfile(const String& profileName) {
|
||||
m_preset.put(profileName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setProfile(const char* profileName) {
|
||||
m_preset.put(profileName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addInput(const String& inputName, const String& inputValue) {
|
||||
m_inputs.put(inputName.c_str(), inputValue.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addInput(const char* inputName, const char* inputValue) {
|
||||
m_inputs.put(inputName, inputValue);
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addInput(const char* inputName, const String& inputValue) {
|
||||
m_inputs.put(inputName, inputValue.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addInput(const String& inputName, const char* inputValue) {
|
||||
m_inputs.put(inputName.c_str(), inputValue);
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addOutputFilter(const char* outputName, const char* filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName, filterPath, variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addOutputFilter(const String& outputName, const char* filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath, variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addOutputFilter(const char* outputName, const String& filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName, filterPath.c_str(), variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addOutputFilter(const String& outputName, const String& filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath.c_str(), variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addOutputFilter(const char* outputName, const char* filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName, filterPath, variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addOutputFilter(const String& outputName, const char* filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath, variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addOutputFilter(const char* outputName, const String& filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName, filterPath.c_str(), variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addOutputFilter(const String& outputName, const String& filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath.c_str(), variableName.c_str());
|
||||
}
|
||||
|
||||
int TembooMQTTChoreo::waitForResponse(volatile bool& var, TembooMQTTSession& session, ArduinoTimer& timer) {
|
||||
|
||||
int rc = TEMBOO_ERROR_OK;
|
||||
|
||||
while (!var) {
|
||||
if (timer.expired()) {
|
||||
rc = TEMBOO_ERROR_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_client.isConnected()) {
|
||||
TEMBOO_TRACELN("DISCO");
|
||||
rc = TEMBOO_ERROR_MQTT_DISCONNECT;
|
||||
break;
|
||||
}
|
||||
m_client.yield(YIELD_TIME_MILLIS);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
void TembooMQTTChoreo::setHTTPResponseCode(char* respCodeStr) {
|
||||
//NOTE: if we run in a true multithreaded environment, this code
|
||||
//will have to be synchronized somehow. Checking the m_haveHttpCode
|
||||
//flag is insufficient to prevent race conditions.
|
||||
if (!m_haveHttpCode) {
|
||||
strncpy(m_httpCodeStr, respCodeStr, sizeof(m_httpCodeStr)-1);
|
||||
m_httpCodeStr[sizeof(m_httpCodeStr)-1] = '\0';
|
||||
m_haveHttpCode = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TembooMQTTChoreo::setResponseData(char* payload, size_t payloadLen) {
|
||||
|
||||
//NOTE: if we run in a true multithreaded environment, this code
|
||||
//will have to be synchronized somehow. Checking the m_haveData
|
||||
//flag is insufficient to prevent race conditions.
|
||||
if (!m_haveData) {
|
||||
size_t len = 0;
|
||||
if(payloadLen > MAX_MESSAGE_SIZE) {
|
||||
m_packetStatus = TEMBOO_ERROR_MQTT_BUFFER_OVERFLOW;
|
||||
len = sizeof(m_respData)/sizeof(m_respData[0])-1;
|
||||
} else if (payloadLen > MAX_RESPONSE_SIZE) {
|
||||
m_packetStatus = TEMBOO_ERROR_MQTT_DATA_TRUNCATED;
|
||||
len = sizeof(m_respData)/sizeof(m_respData[0])-1;
|
||||
} else {
|
||||
len = payloadLen;
|
||||
}
|
||||
memcpy(m_respData, payload, len);
|
||||
m_respData[len] = '\0';
|
||||
m_haveData = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TembooMQTTChoreo::setAckCode(uint16_t ackCode) {
|
||||
//NOTE: if we run in a true multithreaded environment, this code
|
||||
//will have to be synchronized somehow. Checking the m_haveAckCode
|
||||
//flag is insufficient to prevent race conditions.
|
||||
if (!m_haveAckCode) {
|
||||
m_ackCode = ackCode;
|
||||
m_haveAckCode = true;
|
||||
}
|
||||
}
|
||||
|
||||
int TembooMQTTChoreo::run(uint16_t timeoutSecs) {
|
||||
m_nextChar = NULL;
|
||||
|
||||
if (!m_client.isConnected()) {
|
||||
return TEMBOO_ERROR_MQTT_DISCONNECT;
|
||||
}
|
||||
|
||||
if (IS_EMPTY(m_accountName)) {
|
||||
return TEMBOO_ERROR_ACCOUNT_MISSING;
|
||||
}
|
||||
|
||||
if (IS_EMPTY(m_path)) {
|
||||
return TEMBOO_ERROR_CHOREO_MISSING;
|
||||
}
|
||||
|
||||
if (IS_EMPTY(m_appKeyName)) {
|
||||
return TEMBOO_ERROR_APPKEY_NAME_MISSING;
|
||||
}
|
||||
|
||||
if (IS_EMPTY(m_appKeyValue)) {
|
||||
return TEMBOO_ERROR_APPKEY_MISSING;
|
||||
}
|
||||
|
||||
m_packetStatus = TEMBOO_ERROR_OK;
|
||||
|
||||
TembooMQTTSession session(m_client);
|
||||
m_requestId = s_nextRequestId++;
|
||||
|
||||
m_haveAckCode = false;
|
||||
m_ackCode = TEMBOO_ERROR_NO_RESPONSE;
|
||||
|
||||
m_haveHttpCode = false;
|
||||
memset(m_httpCodeStr, '\0', sizeof(m_httpCodeStr)/sizeof(m_httpCodeStr[0]));
|
||||
|
||||
m_haveData = false;
|
||||
memset(m_respData, '\0', sizeof(m_respData)/sizeof(m_respData[0]));
|
||||
|
||||
m_availableChars = 0;
|
||||
m_nextState = START;
|
||||
g_currentChoreo = this;
|
||||
|
||||
TEMBOO_TRACE("RUN ");
|
||||
TEMBOO_TRACE(m_requestId);
|
||||
TEMBOO_TRACELN(" START");
|
||||
|
||||
ArduinoTimer timer(timeoutSecs * 1000L);
|
||||
|
||||
int rc = session.executeChoreo( m_requestId, m_accountName, m_appKeyName, m_appKeyValue, m_path, m_inputs, m_expressions, m_sensors, m_outputs, m_preset, m_deviceType, m_deviceName);
|
||||
if (TEMBOO_ERROR_OK != rc) {
|
||||
goto ErrorExit;
|
||||
}
|
||||
|
||||
rc = waitForResponse(m_haveAckCode, session, timer);
|
||||
if (TEMBOO_ERROR_OK != rc){
|
||||
goto ErrorExit;
|
||||
}
|
||||
|
||||
TEMBOO_TRACE("RUN ");
|
||||
TEMBOO_TRACE(m_requestId);
|
||||
TEMBOO_TRACE(" ACK: ");
|
||||
TEMBOO_TRACELN(m_ackCode);
|
||||
|
||||
if (TEMBOO_ERROR_OK != m_ackCode) {
|
||||
rc = m_ackCode;
|
||||
goto ErrorExit;
|
||||
}
|
||||
|
||||
rc = waitForResponse(m_haveHttpCode, session, timer);
|
||||
if (TEMBOO_ERROR_OK != rc) {
|
||||
goto ErrorExit;
|
||||
}
|
||||
|
||||
TEMBOO_TRACE("RUN ");
|
||||
TEMBOO_TRACE(m_requestId);
|
||||
TEMBOO_TRACE(" RESP: ");
|
||||
TEMBOO_TRACELN(m_httpCodeStr);
|
||||
|
||||
rc = waitForResponse(m_haveData, session, timer);
|
||||
if (TEMBOO_ERROR_OK != rc) {
|
||||
goto ErrorExit;
|
||||
}
|
||||
m_availableChars = strlen(m_respData) + strlen(m_httpCodeStr) + strlen_P(HTTP_CODE_PREFIX) + strlen_P(HTTP_CODE_SUFFIX);
|
||||
|
||||
m_nextChar = HTTP_CODE_PREFIX;
|
||||
|
||||
TEMBOO_TRACE("RUN ");
|
||||
TEMBOO_TRACE(m_requestId);
|
||||
TEMBOO_TRACE(" ");
|
||||
TEMBOO_TRACE(m_availableChars);
|
||||
TEMBOO_TRACELN(" CHARS");
|
||||
TEMBOO_TRACELN(m_respData);
|
||||
|
||||
rc = m_packetStatus;
|
||||
|
||||
ErrorExit:
|
||||
if (TEMBOO_ERROR_OK != rc) {
|
||||
TEMBOO_TRACE("RUN ");
|
||||
TEMBOO_TRACE(m_requestId);
|
||||
TEMBOO_TRACE(" ERROR:");
|
||||
TEMBOO_TRACELN(rc);
|
||||
}
|
||||
g_currentChoreo = NULL;
|
||||
m_client.yield(YIELD_TIME_MILLIS);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int TembooMQTTChoreo::available() {
|
||||
return m_availableChars;
|
||||
}
|
||||
|
||||
|
||||
int TembooMQTTChoreo::peek() {
|
||||
if (m_availableChars) {
|
||||
if (m_nextState == HTTP_CODE_VALUE || m_nextState == RESP_DATA || m_nextState == END) {
|
||||
return (int)*m_nextChar;
|
||||
} else {
|
||||
return (int)pgm_read_byte(m_nextChar);
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int TembooMQTTChoreo::read() {
|
||||
|
||||
if (m_haveData) {
|
||||
int c = 0;
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
m_nextChar = HTTP_CODE_PREFIX;
|
||||
c = (int)pgm_read_byte(m_nextChar++);
|
||||
m_nextState = HTTP_CODE_PRE;
|
||||
break;
|
||||
|
||||
case HTTP_CODE_PRE:
|
||||
c = (int)pgm_read_byte(m_nextChar++);
|
||||
if (pgm_read_byte(m_nextChar) == '\0') {
|
||||
m_nextState = HTTP_CODE_VALUE;
|
||||
m_nextChar = m_httpCodeStr;
|
||||
}
|
||||
break;
|
||||
|
||||
case HTTP_CODE_VALUE:
|
||||
c = (int)(*m_nextChar++);
|
||||
if (*m_nextChar == '\0') {
|
||||
m_nextState = HTTP_CODE_SUF;
|
||||
m_nextChar = HTTP_CODE_SUFFIX;
|
||||
}
|
||||
break;
|
||||
|
||||
case HTTP_CODE_SUF:
|
||||
c = (int)pgm_read_byte(m_nextChar++);
|
||||
if (pgm_read_byte(m_nextChar) == '\0') {
|
||||
m_nextState = RESP_DATA;
|
||||
m_nextChar = m_respData;
|
||||
}
|
||||
break;
|
||||
|
||||
case RESP_DATA:
|
||||
c = (int)(*m_nextChar++);
|
||||
if ((m_availableChars - 1) <= 0) {
|
||||
m_nextState = END;
|
||||
}
|
||||
break;
|
||||
|
||||
case END:
|
||||
default:
|
||||
c = -1;
|
||||
}
|
||||
if (m_availableChars > 0) {
|
||||
m_availableChars--;
|
||||
}
|
||||
return c;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t TembooMQTTChoreo::write(uint8_t data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::flush() {
|
||||
m_nextChar = NULL;
|
||||
m_nextState = END;
|
||||
m_availableChars = 0;
|
||||
}
|
||||
|
||||
249
arduino/libraries/Temboo/src/TembooMQTTEdgeDevice.h
Normal file
249
arduino/libraries/Temboo/src/TembooMQTTEdgeDevice.h
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo MQTT Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOMQTT_H_
|
||||
#define TEMBOOMQTT_H_
|
||||
|
||||
#ifndef TEMBOO_LIBRARY_VERSION
|
||||
#define TEMBOO_LIBRARY_VERSION 2
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// BEGIN ARDUINO NON-YUN SUPPORT
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "utility/MQTTClient.h"
|
||||
#include "utility/ArduinoTimer.h"
|
||||
#include "utility/TembooTags.h"
|
||||
|
||||
#include "utility/TembooMQTTIPStack.h"
|
||||
#include "utility/ChoreoInputSet.h"
|
||||
#include "utility/ChoreoInputExpressionSet.h"
|
||||
#include "utility/ChoreoSensorInputSet.h"
|
||||
#include "utility/ChoreoOutputSet.h"
|
||||
#include "utility/ChoreoPreset.h"
|
||||
#include "utility/ChoreoDevice.h"
|
||||
|
||||
#define IS_EMPTY(s) (NULL == s || '\0' == *s)
|
||||
|
||||
#define TEMBOO_ERROR_OK (0)
|
||||
#define TEMBOO_ERROR_FAILURE (1)
|
||||
#define TEMBOO_ERROR_ACCOUNT_MISSING (201)
|
||||
#define TEMBOO_ERROR_CHOREO_MISSING (203)
|
||||
#define TEMBOO_ERROR_APPKEY_NAME_MISSING (205)
|
||||
#define TEMBOO_ERROR_APPKEY_MISSING (207)
|
||||
#define TEMBOO_ERROR_HTTP_ERROR (223)
|
||||
#define TEMBOO_ERROR_TIMEOUT (225)
|
||||
#define TEMBOO_ERROR_MEMORY (900)
|
||||
#define TEMBOO_ERROR_TCPIP_CONNECT_FAIL (901)
|
||||
#define TEMBOO_ERROR_MQTT_CONNECT_FAIL (902)
|
||||
#define TEMBOO_ERROR_MQTT_SUBSCRIBE_FAIL (903)
|
||||
#define TEMBOO_ERROR_MQTT_DISCONNECT (904)
|
||||
#define TEMBOO_ERROR_DEVICE_ID_MISSING (905)
|
||||
/*
|
||||
* The data from the MQTT is too large to fit in the buffer.
|
||||
* The MQTT read buffer read as much as it could and discarded
|
||||
* the rest of the packet data. Increase MAX_MESSAGE_SIZE
|
||||
* to read more data in MQTT's readbuf
|
||||
*/
|
||||
#define TEMBOO_ERROR_MQTT_BUFFER_OVERFLOW (906)
|
||||
/*
|
||||
* There was more data to be returned in the packet data than could
|
||||
* fit in the return buffer. Incease MAX_RESPONSE_SIZE to read more
|
||||
* of the packet response
|
||||
*/
|
||||
#define TEMBOO_ERROR_MQTT_DATA_TRUNCATED (907)
|
||||
#define TEMBOO_ERROR_NO_RESPONSE (0xFFFF)
|
||||
|
||||
|
||||
|
||||
static const int MAX_MESSAGE_SIZE = 512;
|
||||
static const int MAX_RESPONSE_SIZE = 100;
|
||||
static const int MAX_HANDLERS = 4;
|
||||
static const int YIELD_TIME_MILLIS = 200;
|
||||
|
||||
typedef MQTT::Client<TembooMQTTIPStack, ArduinoTimer, MAX_MESSAGE_SIZE, MAX_HANDLERS> BaseClient;
|
||||
|
||||
class TembooMQTTClient : public BaseClient {
|
||||
public:
|
||||
TembooMQTTClient(TembooMQTTIPStack& ipStack, unsigned int commandTimeoutMs = 30000 );
|
||||
virtual ~TembooMQTTClient();
|
||||
|
||||
using BaseClient::connect;
|
||||
int connect(const char* hostname, int port=1883);
|
||||
|
||||
using BaseClient::isConnected;
|
||||
bool isConnected();
|
||||
|
||||
int sendChoreoRequest(const char* request, size_t len);
|
||||
int setDeviceId(char* id);
|
||||
int setDeviceIdFromMac(byte (&mac)[6]);
|
||||
|
||||
protected:
|
||||
TembooMQTTIPStack& m_ipStack;
|
||||
char* m_deviceId;
|
||||
char* m_requestTopic;
|
||||
char* m_ackTopic;
|
||||
char* m_responseTopic;
|
||||
char* m_dataTopic;
|
||||
|
||||
char* strCatNew_P(const char*, const char*);
|
||||
void makeTopics();
|
||||
};
|
||||
|
||||
class TembooMQTTSession;
|
||||
|
||||
class TembooMQTTChoreo : public Stream {
|
||||
public:
|
||||
|
||||
// Constructor.
|
||||
// client - an instance of a TembooMQTTClient.
|
||||
// Used to communicate with a Temboo Gateway.
|
||||
TembooMQTTChoreo(TembooMQTTClient& client);
|
||||
~TembooMQTTChoreo();
|
||||
|
||||
// Does nothing. Just for source compatibility with Yun code.
|
||||
void begin() {};
|
||||
|
||||
// Sets the account name to use when communicating with Temboo.
|
||||
// (required)
|
||||
void setAccountName(const String& accountName);
|
||||
void setAccountName(const char* accountName);
|
||||
|
||||
// Sets the application key name to use with choreo execution requests.
|
||||
// (required)
|
||||
void setAppKeyName(const String& appKeyName);
|
||||
void setAppKeyName(const char* appKeyName);
|
||||
|
||||
// Sets the application key value to use with choreo execution requests
|
||||
// (required)
|
||||
void setAppKey(const String& appKey);
|
||||
void setAppKey(const char* appKey);
|
||||
|
||||
// sets the name of the choreo to be executed.
|
||||
// (required)
|
||||
void setChoreo(const String& choreoPath);
|
||||
void setChoreo(const char* choreoPath);
|
||||
|
||||
|
||||
// sets the name of the saved inputs to use when executing the choreo
|
||||
// (optional)
|
||||
void setSavedInputs(const String& savedInputsName);
|
||||
void setSavedInputs(const char* savedInputsName);
|
||||
|
||||
void setCredential(const String& credentialName);
|
||||
void setCredential(const char* credentialName);
|
||||
|
||||
void setProfile(const String& profileName);
|
||||
void setProfile(const char* profileName);
|
||||
|
||||
// sets an input to be used when executing a choreo.
|
||||
// (optional or required, depending on the choreo being executed.)
|
||||
void addInput(const String& inputName, const String& inputValue);
|
||||
void addInput(const char* inputName, const char* inputValue);
|
||||
void addInput(const char* inputName, const String& inputValue);
|
||||
void addInput(const String& inputName, const char* inputValue);
|
||||
|
||||
// sets an output filter to be used to process the choreo output
|
||||
// (optional)
|
||||
void addOutputFilter(const char* filterName, const char* filterPath, const char* variableName);
|
||||
void addOutputFilter(const String& filterName, const char* filterPath, const char* variableName);
|
||||
void addOutputFilter(const char* filterName, const String& filterPath, const char* variableName);
|
||||
void addOutputFilter(const String& filterName, const String& filterPath, const char* variableName);
|
||||
void addOutputFilter(const char* filterName, const char* filterPath, const String& variableName);
|
||||
void addOutputFilter(const String& filterName, const char* filterPath, const String& variableName);
|
||||
void addOutputFilter(const char* filterName, const String& filterPath, const String& variableName);
|
||||
void addOutputFilter(const String& filterName, const String& filterPath, const String& variableName);
|
||||
|
||||
// run the choreo using the current input info
|
||||
int run(uint16_t timeoutSecs);
|
||||
|
||||
char* getResponseData() {return m_respData;}
|
||||
char* getHTTPResponseCode() {return m_httpCodeStr;}
|
||||
|
||||
|
||||
// Stream interface - see the Arduino library documentation.
|
||||
void close() {};
|
||||
int available();
|
||||
int read();
|
||||
int peek();
|
||||
void flush();
|
||||
|
||||
//Print interface - see the Arduino library documentation
|
||||
size_t write(uint8_t data);
|
||||
|
||||
|
||||
protected:
|
||||
TembooMQTTClient& m_client;
|
||||
const char* m_accountName;
|
||||
const char* m_appKeyName;
|
||||
const char* m_appKeyValue;
|
||||
const char* m_path;
|
||||
|
||||
ChoreoInputSet m_inputs;
|
||||
ChoreoInputExpressionSet m_expressions;
|
||||
ChoreoSensorInputSet m_sensors;
|
||||
ChoreoOutputSet m_outputs;
|
||||
ChoreoPreset m_preset;
|
||||
ChoreoDevice m_deviceType;
|
||||
ChoreoDevice m_deviceName;
|
||||
|
||||
char m_httpCodeStr[4];
|
||||
volatile bool m_haveHttpCode;
|
||||
|
||||
uint16_t m_ackCode;
|
||||
volatile bool m_haveAckCode;
|
||||
|
||||
char m_respData[MAX_RESPONSE_SIZE+1];
|
||||
volatile bool m_haveData;
|
||||
|
||||
uint16_t m_requestId;
|
||||
static uint16_t s_nextRequestId;
|
||||
|
||||
volatile uint16_t m_packetStatus;
|
||||
|
||||
// variables for the stream interface
|
||||
size_t m_availableChars;
|
||||
const char* m_nextChar;
|
||||
enum State {START, HTTP_CODE_PRE, HTTP_CODE_VALUE, HTTP_CODE_SUF, RESP_DATA, END};
|
||||
State m_nextState;
|
||||
|
||||
protected:
|
||||
uint16_t getRequestId() {return m_requestId;}
|
||||
void setAckCode(uint16_t ackCode);
|
||||
void setHTTPResponseCode(char* respCode);
|
||||
void setResponseData(char* data, size_t len);
|
||||
|
||||
int waitForResponse(volatile bool& var, TembooMQTTSession& session, ArduinoTimer& timer);
|
||||
|
||||
friend void handleDataMessage(MQTT::MessageData& md);
|
||||
friend void handleResponseMessage(MQTT::MessageData& md);
|
||||
friend void handleAckMessage(MQTT::MessageData& md);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //TEMBOO_H_
|
||||
267
arduino/libraries/Temboo/src/TembooMonitoring.cpp
Normal file
267
arduino/libraries/Temboo/src/TembooMonitoring.cpp
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Mailbox.h>
|
||||
#if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM)
|
||||
#include "avr/dtostrf.h"
|
||||
#endif
|
||||
#include "utility/TembooGlobal.h"
|
||||
#include "TembooMonitoring.h"
|
||||
|
||||
const int MAX_MAILBOX_MESSAGE_SIZE = 128;
|
||||
|
||||
const unsigned long POLL_TIMEOUT = 50;
|
||||
const unsigned long INITIATE_TIMEOUT_MS = 5000;
|
||||
const unsigned long MCU_PING_PERIOD_MS = 5000;
|
||||
|
||||
void logTembooDebug(const char *c) {
|
||||
Console.print("Debug: ");
|
||||
Console.println(c);
|
||||
}
|
||||
|
||||
void addWebSocketPinData(int pin, int pinVal, bool requestResponse) {
|
||||
TembooMessaging::sendData(pin, pinVal, requestResponse);
|
||||
}
|
||||
|
||||
void updateIntervalTime(int intervalTime) {
|
||||
uint8_t msg[MAX_MAILBOX_MESSAGE_SIZE] = {0};
|
||||
int messageSize = snprintf((char*)msg, MAX_MAILBOX_MESSAGE_SIZE, "Mi|V%i", intervalTime);
|
||||
Mailbox.writeMessage(msg, messageSize);
|
||||
}
|
||||
|
||||
TembooMessaging::TembooMessaging(TembooSensor** sensorTable, int sensorTableSize) {
|
||||
m_accountName = NULL;
|
||||
m_appKey = NULL;
|
||||
m_appKeyName = NULL;
|
||||
m_deviceID = NULL;
|
||||
m_connectionStatus = false;
|
||||
m_sensorTable = sensorTable;
|
||||
m_sensorTableSize = sensorTableSize;
|
||||
m_sensorTableDepth = 0;
|
||||
m_connectionAttemptTime = millis();
|
||||
m_lastPingTime = millis();
|
||||
// init sensor array
|
||||
int i = 0;
|
||||
for (i = 0; i < m_sensorTableSize; i++) {
|
||||
m_sensorTable[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int TembooMessaging::addTembooSensor(TembooSensor* sensor) {
|
||||
int i = 0;
|
||||
for (; i < m_sensorTableSize; i++) {
|
||||
if (m_sensorTable[i] == sensor) {
|
||||
logTembooDebug("Sensor already added");
|
||||
return -1;
|
||||
}
|
||||
if (m_sensorTable[i] == NULL) {
|
||||
m_sensorTable[i] = sensor;
|
||||
m_sensorTableDepth++;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
logTembooDebug("Sensor table full, sensor not added");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void TembooMessaging::startMessaging() {
|
||||
if (!running()) {
|
||||
TEMBOO_TRACELN("starting messanger");
|
||||
m_connectionStatus = false;
|
||||
Process::begin("tembooMessaging");
|
||||
runAsynchronously();
|
||||
}
|
||||
}
|
||||
|
||||
void TembooMessaging::setSensorsToDefaultState() {
|
||||
int i = 0;
|
||||
for (; i < m_sensorTableDepth; i++) {
|
||||
if (m_sensorTable[i]->write != NULL) {
|
||||
m_sensorTable[i]->write(m_sensorTable[i]->sensorConfig, m_sensorTable[i]->defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TembooMessaging::begin() {
|
||||
Mailbox.begin();
|
||||
startMessaging();
|
||||
}
|
||||
|
||||
void TembooMessaging::setAccountName(const String& accountName) {
|
||||
m_accountName = accountName.c_str();
|
||||
}
|
||||
void TembooMessaging::setAccountName(const char* accountName) {
|
||||
m_accountName = accountName;
|
||||
}
|
||||
|
||||
void TembooMessaging::setAppKeyName(const String& appKeyName) {
|
||||
m_appKeyName = appKeyName.c_str();
|
||||
}
|
||||
void TembooMessaging::setAppKeyName(const char* appKeyName) {
|
||||
m_appKeyName = appKeyName;
|
||||
}
|
||||
|
||||
void TembooMessaging::setAppKey(const String& appKey) {
|
||||
m_appKey = appKey.c_str();
|
||||
}
|
||||
void TembooMessaging::setAppKey(const char* appKey) {
|
||||
m_appKey = appKey;
|
||||
}
|
||||
|
||||
void TembooMessaging::setDeviceID(const String& deviceID) {
|
||||
m_deviceID = deviceID.c_str();
|
||||
}
|
||||
void TembooMessaging::setDeviceID(const char* deviceID) {
|
||||
m_deviceID = deviceID;
|
||||
}
|
||||
|
||||
int TembooMessaging::initiateConnection() {
|
||||
unsigned long now = millis();
|
||||
if (now - m_connectionAttemptTime < INITIATE_TIMEOUT_MS) {
|
||||
poll();
|
||||
return TEMBOO_MONITORING_ERROR_NOT_CONNECTION_TIME;
|
||||
}
|
||||
if (m_accountName == NULL || *m_accountName == '\0') {
|
||||
return TEMBOO_MONITORING_ERROR_ACCOUNT_MISSING;
|
||||
}
|
||||
if (m_appKeyName == NULL || *m_appKeyName == '\0') {
|
||||
|
||||
return TEMBOO_MONITORING_ERROR_APPKEY_NAME_MISSING;
|
||||
}
|
||||
if (m_deviceID == NULL || *m_deviceID == '\0') {
|
||||
return TEMBOO_MONITORING_ERROR_DEVICEID_MISSING;
|
||||
}
|
||||
if (m_appKey == NULL || *m_appKey == '\0') {
|
||||
return TEMBOO_MONITORING_ERROR_APPKEY_MISSING;
|
||||
}
|
||||
startMessaging();
|
||||
int messageSize = strlen(m_accountName) + strlen(m_appKey) + strlen(m_appKeyName) + strlen(m_deviceID) + 11;
|
||||
uint8_t msg[messageSize];
|
||||
if (messageSize < MAX_MAILBOX_MESSAGE_SIZE) {
|
||||
messageSize = snprintf((char*)msg, messageSize, "MI|N%s|K%s|B%s|A%s", m_accountName, m_appKeyName, m_deviceID, m_appKey);
|
||||
Mailbox.writeMessage(msg, messageSize);
|
||||
m_connectionAttemptTime = now;
|
||||
} else {
|
||||
return TEMBOO_MONITORING_ERROR_REQUEST_TOO_LARGE;
|
||||
}
|
||||
return TEMBOO_MONITORING_ERROR_OK;
|
||||
}
|
||||
|
||||
WSMessageRequest TembooMessaging::poll() {
|
||||
startMessaging();
|
||||
long int now = millis();
|
||||
WSMessageRequest rc = WS_NO_MESSAGE;
|
||||
while (millis() - now < POLL_TIMEOUT) {
|
||||
if (millis() - m_lastPingTime >= MCU_PING_PERIOD_MS) {
|
||||
m_lastPingTime = millis();
|
||||
sendPing();
|
||||
}
|
||||
if (Mailbox.messageAvailable()) {
|
||||
uint8_t msg[MAX_MAILBOX_MESSAGE_SIZE] = {0};
|
||||
int recvLen = Mailbox.readMessage(msg, MAX_MAILBOX_MESSAGE_SIZE);
|
||||
if (recvLen > 0) {
|
||||
rc = handleResponse(msg, m_sensorTable, m_sensorTableDepth, m_connectionStatus);
|
||||
if (rc == WS_UPDATE_CONNECTED) {
|
||||
//logTembooDebug("Connected to Temboo");
|
||||
m_connectionStatus = true;
|
||||
} else if (rc == WS_UPDATE_DISCONNECTED) {
|
||||
//logTembooDebug("Disconnected from Temboo");
|
||||
m_connectionStatus = false;
|
||||
} else if (rc == WS_REQUEST_ERROR) {
|
||||
// disconnect
|
||||
sendError("Message request error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
void TembooMessaging::updatePinValue(int pinNum, int pinVal) {
|
||||
// save the data to the strcuture and then send to Temboo
|
||||
int i = 0;
|
||||
for (; i < m_sensorTableDepth; i++) {
|
||||
if (m_sensorTable[i]->getSensorPin(m_sensorTable[i]->sensorConfig) == pinNum) {
|
||||
// if pin has pinWrite as NULL, it is an input
|
||||
// pin and needs to be stored. If not NULL,
|
||||
// pin is an actuator and should not be stored
|
||||
if(m_sensorTable[i]->write == NULL){
|
||||
sendData(pinNum, pinVal, false);
|
||||
} else {
|
||||
sendData(pinNum, pinVal, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
logTembooDebug("Unable to update pin");
|
||||
}
|
||||
|
||||
int TembooMessaging::retrievePinValue(int pinNum) {
|
||||
// search through pin structure and return the pin value
|
||||
int i = 0;
|
||||
for (; i < m_sensorTableDepth; i++) {
|
||||
if (m_sensorTable[i]->getSensorPin(m_sensorTable[i]->sensorConfig) == pinNum) {
|
||||
return m_sensorTable[i]->read(m_sensorTable[i]->sensorConfig);
|
||||
}
|
||||
}
|
||||
logTembooDebug("Unable to obtain pin value");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TembooMessaging::sendError(const char* errorText) {
|
||||
uint8_t msg[MAX_MAILBOX_MESSAGE_SIZE] = {0};
|
||||
int messageSize = snprintf((char*)msg, MAX_MAILBOX_MESSAGE_SIZE, "ME|T%s", errorText);
|
||||
Mailbox.writeMessage(msg, messageSize);
|
||||
}
|
||||
|
||||
void TembooMessaging::disconnectWSConnection(int closeCode, const char* closeReason) {
|
||||
int messageSize = strlen(closeReason) + 11;
|
||||
uint8_t msg[messageSize];
|
||||
messageSize = snprintf((char*)msg, messageSize, "MF|O%i|r%s", closeCode, closeReason);
|
||||
Mailbox.writeMessage(msg, messageSize);
|
||||
}
|
||||
|
||||
void TembooMessaging::sendData(int pin, int pinVal, bool requestResponse) {
|
||||
uint8_t msg[MAX_MAILBOX_MESSAGE_SIZE] = {0};
|
||||
int messageSize = snprintf((char*)msg, MAX_MAILBOX_MESSAGE_SIZE, "MD|P%i|V%i%s", pin, pinVal, requestResponse ? "|Q" : "");
|
||||
Mailbox.writeMessage(msg, messageSize);
|
||||
}
|
||||
void TembooMessaging::sendData(int pin, float pinVal) {
|
||||
uint8_t msg[MAX_MAILBOX_MESSAGE_SIZE] = {0};
|
||||
char floatStr[12] = {0};
|
||||
dtostrf(pinVal, 4, 2, floatStr);
|
||||
int messageSize = snprintf((char*)msg, MAX_MAILBOX_MESSAGE_SIZE, "MD|P%i|V%s", pin, floatStr);
|
||||
Mailbox.writeMessage(msg, messageSize);
|
||||
}
|
||||
|
||||
bool TembooMessaging::isConnected() {
|
||||
if (running()) {
|
||||
return m_connectionStatus;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TembooMessaging::sendPing() {
|
||||
Mailbox.writeMessage((uint8_t*)"P",1);
|
||||
}
|
||||
95
arduino/libraries/Temboo/src/TembooMonitoring.h
Normal file
95
arduino/libraries/Temboo/src/TembooMonitoring.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOMESSAGING_H_
|
||||
#define TEMBOOMESSAGING_H_
|
||||
|
||||
#ifndef TEMBOO_LIBRARY_VERSION
|
||||
#define TEMBOO_LIBRARY_VERSION 2
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Process.h>
|
||||
|
||||
#include "utility/TembooWebSocketRequestHandles.h"
|
||||
|
||||
#define TEMBOO_MONITORING_ERROR_OK (0)
|
||||
#define TEMBOO_MONITORING_ERROR_ACCOUNT_MISSING (201)
|
||||
#define TEMBOO_MONITORING_ERROR_CHOREO_MISSING (203)
|
||||
#define TEMBOO_MONITORING_ERROR_APPKEY_NAME_MISSING (205)
|
||||
#define TEMBOO_MONITORING_ERROR_APPKEY_MISSING (207)
|
||||
#define TEMBOO_MONITORING_ERROR_DEVICEID_MISSING (209)
|
||||
|
||||
#define TEMBOO_MONITORING_ERROR_REQUEST_TOO_LARGE (251)
|
||||
#define TEMBOO_MONITORING_ERROR_NOT_CONNECTION_TIME (253)
|
||||
|
||||
class TembooMessaging : public Process {
|
||||
|
||||
public:
|
||||
TembooMessaging(TembooSensor** sensors, int pinTableSize);
|
||||
void begin();
|
||||
|
||||
void setAccountName(const String& accountName);
|
||||
void setAccountName(const char* accountName);
|
||||
|
||||
void setAppKeyName(const String& appKeyName);
|
||||
void setAppKeyName(const char* appKeyName);
|
||||
|
||||
void setAppKey(const String& appKey);
|
||||
void setAppKey(const char* appKey);
|
||||
|
||||
void setDeviceID(const String& appKey);
|
||||
void setDeviceID(const char* appKey);
|
||||
|
||||
int initiateConnection();
|
||||
|
||||
WSMessageRequest poll();
|
||||
|
||||
static void sendData(int pin, int pinVal, bool requestResponse=false);
|
||||
void sendData(int pin, float pinVal);
|
||||
void updatePinValue(int pinNum, int pinVal);
|
||||
int retrievePinValue(int pinNum);
|
||||
int addTembooSensor(TembooSensor* sensor);
|
||||
void setSensorsToDefaultState();
|
||||
|
||||
bool isConnected();
|
||||
|
||||
private:
|
||||
const char* m_accountName;
|
||||
const char* m_appKey;
|
||||
const char* m_appKeyName;
|
||||
const char* m_deviceID;
|
||||
bool m_connectionStatus;
|
||||
int m_sensorTableSize;
|
||||
int m_sensorTableDepth;
|
||||
long int m_connectionAttemptTime;
|
||||
TembooSensor** m_sensorTable;
|
||||
unsigned long m_lastPingTime;
|
||||
|
||||
void startMessaging();
|
||||
void disconnectWSConnection(int closeCode, const char* closeReason);
|
||||
void sendError(const char* errorText);
|
||||
void sendPing();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
51
arduino/libraries/Temboo/src/TembooSSL.cpp
Normal file
51
arduino/libraries/Temboo/src/TembooSSL.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#if defined (ARDUINO_AVR_YUN) || defined (ARDUINO_AVR_TRE)
|
||||
|
||||
#else
|
||||
|
||||
#include <string.h>
|
||||
#include <Client.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include <TembooSSL.h>
|
||||
#include "utility/TembooGlobal.h"
|
||||
#include "utility/TembooSession.h"
|
||||
|
||||
TembooChoreoSSL::TembooChoreoSSL(Client& client) : TembooChoreo(client) {
|
||||
m_accountName = NULL;
|
||||
m_appKeyName = NULL;
|
||||
m_appKeyValue = NULL;
|
||||
m_path = NULL;
|
||||
m_nextChar = NULL;
|
||||
m_nextState = END;
|
||||
}
|
||||
|
||||
int TembooChoreoSSL::run() {
|
||||
return TembooChoreo::run(INADDR_NONE, 443, TEMBOO_CHOREO_DEFAULT_TIMEOUT_SECS);
|
||||
}
|
||||
|
||||
int TembooChoreoSSL::run(uint16_t timeoutSecs) {
|
||||
return TembooChoreo::run(INADDR_NONE, 443, timeoutSecs);
|
||||
}
|
||||
|
||||
#endif
|
||||
70
arduino/libraries/Temboo/src/TembooSSL.h
Normal file
70
arduino/libraries/Temboo/src/TembooSSL.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOSSL_H_
|
||||
#define TEMBOOSSL_H_
|
||||
|
||||
#ifndef TEMBOO_LIBRARY_VERSION
|
||||
#define TEMBOO_LIBRARY_VERSION 2
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#if defined (ARDUINO_AVR_YUN) || defined (ARDUINO_AVR_TRE)
|
||||
|
||||
#include <Temboo.h>
|
||||
|
||||
class TembooChoreoSSL : public TembooChoreo {};
|
||||
|
||||
#else
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// BEGIN ARDUINO NON-YUN SUPPORT FOR SSL
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#include <Stream.h>
|
||||
#include <Client.h>
|
||||
#include <IPAddress.h>
|
||||
#include <Temboo.h>
|
||||
#include "utility/ChoreoInputSet.h"
|
||||
#include "utility/ChoreoOutputSet.h"
|
||||
#include "utility/ChoreoPreset.h"
|
||||
|
||||
class TembooChoreoSSL : public TembooChoreo {
|
||||
public:
|
||||
|
||||
// Constructor.
|
||||
// client - an instance of an Arduino Client that
|
||||
// allows HTTPS conntections, usually WiFiSSL
|
||||
TembooChoreoSSL(Client& client);
|
||||
|
||||
// run the choreo using the current input info
|
||||
// uses port 443
|
||||
int run();
|
||||
// run the choreo with a user specified timeout
|
||||
// uses port 443
|
||||
int run(uint16_t timeoutSecs);
|
||||
};
|
||||
|
||||
#endif //ARDUINO_AVR_YUN
|
||||
|
||||
#endif //TEMBOO_H_
|
||||
70
arduino/libraries/Temboo/src/TembooYunShield.h
Normal file
70
arduino/libraries/Temboo/src/TembooYunShield.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOYUNSHIELD_H_
|
||||
#define TEMBOOYUNSHIELD_H_
|
||||
|
||||
#ifndef TEMBOO_LIBRARY_VERSION
|
||||
#define TEMBOO_LIBRARY_VERSION 2
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// BEGIN ARDUINO YUN SHIELD SUPPORT
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#include <Process.h>
|
||||
|
||||
class TembooYunShieldChoreo : public Process {
|
||||
|
||||
public:
|
||||
void begin() {Process::begin("temboo");}
|
||||
void setAccountName(const String& accountName) { addParameter("-a" + accountName);}
|
||||
void setAppKeyName(const String& appKeyName) { addParameter("-u" + appKeyName);}
|
||||
void setAppKey(const String& appKey) { addParameter("-p" + appKey);}
|
||||
void setChoreo(const String& choreo) { addParameter("-c" + choreo);}
|
||||
void setCredential(const String& credentialName) { addParameter("-e" + credentialName);}
|
||||
void setSavedInputs(const String& savedInputsName) { addParameter("-e" + savedInputsName);}
|
||||
void setProfile(const String& profileName) { addParameter("-e" + profileName);}
|
||||
void addInput(const String& inputName, const String& inputValue) { addParameter("-i" + inputName + ":" + inputValue);}
|
||||
void addOutputFilter(const String& filterName, const String& filterPath, const String& variableName) { addParameter("-o" + filterName + ":" + filterPath + ":" + variableName);}
|
||||
void setSettingsFileToWrite(const String& filePath) { addParameter("-w" + filePath);}
|
||||
void setSettingsFileToRead(const String& filePath) { addParameter("-r" + filePath);}
|
||||
void setGatewayAddress(const String& addr) { addParameter("-s" + addr);}
|
||||
void addInputExpression(const String& inputName, const String& inputValue) { addParameter("-f" + inputName + ":" + inputValue);}
|
||||
void addInputWithSensor(const String& inputName, const String& inputValue) { addParameter("-f" + inputName + ":" + inputValue);}
|
||||
void addSensorInput(const String& sensorName, long sensorValue, const String& conversion) {addParameter("-n" + sensorName + ":" + String(sensorValue) + ":" + conversion);}
|
||||
void addSensorInput(const String& sensorName, long sensorValue) {addParameter("-v" + sensorName + ":" + String(sensorValue));}
|
||||
void addSensorInput(const String& sensorName, long sensorValue, const String& conversion, const String& calibrationValue) {addParameter("-b" + sensorName + ":" + String(sensorValue) + ":" + conversion + ":" + calibrationValue);}
|
||||
void addSensorInput(const String& sensorName, long sensorValue, const String& rawLow, const String& rawHigh, const String& scaleLow, const String& scaleHigh) {addParameter("-m" + sensorName + ":" + String(sensorValue) + ":" + rawLow+ ":" + rawHigh+ ":" + scaleLow+ ":" + scaleHigh);}
|
||||
void addSensorValue(const String& sensorName, long sensorValue, const String& conversion) {addParameter("-n" + sensorName + ":" + String(sensorValue) + ":" + conversion);}
|
||||
void addSensorValue(const String& sensorName, long sensorValue) {addParameter("-v" + sensorName + ":" + String(sensorValue));}
|
||||
void addSensorValue(const String& sensorName, long sensorValue, const String& conversion, const String& calibrationValue) {addParameter("-b" + sensorName + ":" + String(sensorValue) + ":" + conversion + ":" + calibrationValue);}
|
||||
void addSensorValue(const String& sensorName, long sensorValue, const String& rawLow, const String& rawHigh, const String& scaleLow, const String& scaleHigh) {addParameter("-m" + sensorName + ":" + String(sensorValue) + ":" + rawLow+ ":" + rawHigh+ ":" + scaleLow+ ":" + scaleHigh);}
|
||||
void setDeviceName(const String& deviceName) {addParameter("-d" + deviceName);}
|
||||
void setDeviceType(const String& deviceType) {addParameter("-t" + deviceType);}
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //TEMBOOYUNSHIELD_H_
|
||||
62
arduino/libraries/Temboo/src/utility/ArduinoTimer.h
Normal file
62
arduino/libraries/Temboo/src/utility/ArduinoTimer.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo MQTT edge device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef ARDUINOTIMER_H_
|
||||
#define ARDUINOTIMER_H_
|
||||
|
||||
class ArduinoTimer {
|
||||
public:
|
||||
ArduinoTimer() {
|
||||
this->startTimeMillis = 0;
|
||||
this->durationMillis = 0;
|
||||
}
|
||||
|
||||
ArduinoTimer(unsigned long durationMillis) {
|
||||
this->countdown_ms(durationMillis);
|
||||
}
|
||||
|
||||
bool expired() {
|
||||
return left_ms() == 0;
|
||||
}
|
||||
|
||||
void countdown_ms(unsigned long durationMillis) {
|
||||
this->startTimeMillis = millis();
|
||||
this->durationMillis = durationMillis;
|
||||
}
|
||||
|
||||
void countdown(int durationSeconds) {
|
||||
countdown_ms((unsigned long)durationSeconds * 1000L);
|
||||
}
|
||||
|
||||
unsigned long left_ms() {
|
||||
unsigned long elapsedMillis = millis() - this->startTimeMillis;
|
||||
return elapsedMillis < this->durationMillis ? (this->durationMillis - elapsedMillis) : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned long startTimeMillis;
|
||||
unsigned long durationMillis;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif /* ARDUINOIMER_H_ */
|
||||
96
arduino/libraries/Temboo/src/utility/BaseFormatter.cpp
Normal file
96
arduino/libraries/Temboo/src/utility/BaseFormatter.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include "BaseFormatter.h"
|
||||
|
||||
char BaseFormatter::escape(char c) {
|
||||
char outChar = c;
|
||||
switch(c) {
|
||||
case '\\':
|
||||
case '"':
|
||||
outChar = '\\';
|
||||
m_escapedChar = c;
|
||||
break;
|
||||
case '\b':
|
||||
outChar = '\\';
|
||||
m_escapedChar = 'b';
|
||||
break;
|
||||
case '\f':
|
||||
outChar = '\\';
|
||||
m_escapedChar = 'f';
|
||||
break;
|
||||
case '\n':
|
||||
outChar = '\\';
|
||||
m_escapedChar = 'n';
|
||||
break;
|
||||
case '\r':
|
||||
outChar = '\\';
|
||||
m_escapedChar = 'r';
|
||||
break;
|
||||
case '\t':
|
||||
outChar = '\\';
|
||||
m_escapedChar = 't';
|
||||
break;
|
||||
default:
|
||||
m_escapedChar = '\0';
|
||||
}
|
||||
return outChar;
|
||||
}
|
||||
|
||||
char BaseFormatter::finishEscape() {
|
||||
char c = m_escapedChar;
|
||||
m_escapedChar = '\0';
|
||||
return c;
|
||||
}
|
||||
|
||||
char BaseFormatter::readTagChar(int nextState) {
|
||||
char c = pgm_read_byte(m_nextChar++);
|
||||
if (pgm_read_byte(m_nextChar) == '\0') {
|
||||
m_nextState = nextState;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
char BaseFormatter::readValueChar(int nextState) {
|
||||
char c;
|
||||
if (isEscaping()) {
|
||||
c = finishEscape();
|
||||
if (*m_nextChar == '\0') {
|
||||
m_nextState = nextState;
|
||||
}
|
||||
} else {
|
||||
c = escape(*m_nextChar++);
|
||||
if (!isEscaping()) {
|
||||
if(*m_nextChar == '\0') {
|
||||
m_nextState = nextState;
|
||||
}
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
char BaseFormatter::readStartTagChar(const char* tag, int nextState) {
|
||||
m_nextChar = tag;
|
||||
char c = pgm_read_byte(m_nextChar++);
|
||||
m_nextState = nextState;
|
||||
return c;
|
||||
}
|
||||
46
arduino/libraries/Temboo/src/utility/BaseFormatter.h
Normal file
46
arduino/libraries/Temboo/src/utility/BaseFormatter.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef BASEFORMATTER_H_
|
||||
#define BASEFORMATTER_H_
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
class BaseFormatter {
|
||||
public:
|
||||
BaseFormatter() {m_escapedChar = '\0';}
|
||||
|
||||
protected:
|
||||
const char* m_nextChar;
|
||||
int m_nextState;
|
||||
char m_escapedChar;
|
||||
|
||||
char escape(char c);
|
||||
bool isEscaping() {return m_escapedChar != '\0';}
|
||||
char finishEscape();
|
||||
|
||||
char readTagChar(int nextState);
|
||||
char readValueChar(int nextState);
|
||||
char readStartTagChar(const char* tag, int nextState);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
23
arduino/libraries/Temboo/src/utility/ChoreoDevice.cpp
Normal file
23
arduino/libraries/Temboo/src/utility/ChoreoDevice.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include "ChoreoDevice.h"
|
||||
42
arduino/libraries/Temboo/src/utility/ChoreoDevice.h
Normal file
42
arduino/libraries/Temboo/src/utility/ChoreoDevice.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREODEVICE_H_
|
||||
#define CHOREODEVICE_H_
|
||||
#include <stddef.h>
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
// Can be used for device type or device name
|
||||
class ChoreoDevice {
|
||||
public:
|
||||
ChoreoDevice() {m_name = NULL;}
|
||||
ChoreoDevice(const char* name) {put(name);}
|
||||
const char* getName() const {return m_name;}
|
||||
void put(const char* name) {m_name = name;}
|
||||
bool isEmpty() const {return m_name == NULL || *m_name == '\0';}
|
||||
|
||||
private:
|
||||
const char* m_name;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "ChoreoDeviceFormatter.h"
|
||||
#include "ChoreoDevice.h"
|
||||
|
||||
static const char TAG_DEVICE[] PROGMEM = "\"deviceType\":";
|
||||
static const char TAG_DEVICE_NAME[] PROGMEM = "\"device\":";
|
||||
|
||||
ChoreoDeviceFormatter::ChoreoDeviceFormatter(const ChoreoDevice* device, Type type) {
|
||||
m_device = device;
|
||||
m_type = type;
|
||||
reset();
|
||||
}
|
||||
|
||||
void ChoreoDeviceFormatter::reset() {
|
||||
m_nextChar = NULL;
|
||||
if (m_device == NULL || m_device->isEmpty()) {
|
||||
m_nextState = END;
|
||||
} else {
|
||||
m_nextState = START;
|
||||
}
|
||||
}
|
||||
|
||||
bool ChoreoDeviceFormatter::hasNext() {
|
||||
return m_nextState != END;
|
||||
}
|
||||
|
||||
char ChoreoDeviceFormatter::next() {
|
||||
char c = '\0';
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
if (m_type == DEVICE_TYPE) {
|
||||
c = readStartTagChar(TAG_DEVICE, DEVICE_TAG);
|
||||
} else {
|
||||
c = readStartTagChar(TAG_DEVICE_NAME, DEVICE_TAG);
|
||||
}
|
||||
break;
|
||||
|
||||
case DEVICE_TAG:
|
||||
c = readTagChar(NAME_START);
|
||||
break;
|
||||
|
||||
case NAME_START:
|
||||
c = '"';
|
||||
m_nextChar = m_device->getName();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = NAME_END;
|
||||
} else {
|
||||
m_nextState = NAME;
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME:
|
||||
c = readValueChar(NAME_END);
|
||||
break;
|
||||
|
||||
case NAME_END:
|
||||
c = '"';
|
||||
m_nextState = END;
|
||||
break;
|
||||
|
||||
case END:
|
||||
default:
|
||||
c = '\0';
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
55
arduino/libraries/Temboo/src/utility/ChoreoDeviceFormatter.h
Normal file
55
arduino/libraries/Temboo/src/utility/ChoreoDeviceFormatter.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREODEVICEFORMATTER_H_
|
||||
#define CHOREODEVICEFORMATTER_H_
|
||||
#include "TembooGlobal.h"
|
||||
#include "BaseFormatter.h"
|
||||
#include "ChoreoDevice.h"
|
||||
|
||||
class ChoreoDeviceFormatter : public BaseFormatter {
|
||||
public:
|
||||
enum Type {
|
||||
DEVICE_TYPE,
|
||||
DEVICE_NAME
|
||||
};
|
||||
|
||||
ChoreoDeviceFormatter(const ChoreoDevice* device, Type type);
|
||||
bool hasNext();
|
||||
char next();
|
||||
void reset();
|
||||
|
||||
protected:
|
||||
enum State {
|
||||
START,
|
||||
DEVICE_TAG,
|
||||
NAME_START,
|
||||
NAME,
|
||||
NAME_END,
|
||||
END
|
||||
};
|
||||
|
||||
const ChoreoDevice* m_device;
|
||||
Type m_type;
|
||||
};
|
||||
|
||||
#endif
|
||||
34
arduino/libraries/Temboo/src/utility/ChoreoInput.cpp
Normal file
34
arduino/libraries/Temboo/src/utility/ChoreoInput.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "ChoreoInput.h"
|
||||
|
||||
ChoreoInput::ChoreoInput(ChoreoInput* prev, const char* name, const char* value) {
|
||||
if (prev != NULL) {
|
||||
prev->m_next = this;
|
||||
}
|
||||
m_next = NULL;
|
||||
m_name = name;
|
||||
m_value = value;
|
||||
}
|
||||
|
||||
41
arduino/libraries/Temboo/src/utility/ChoreoInput.h
Normal file
41
arduino/libraries/Temboo/src/utility/ChoreoInput.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOINPUT_H_
|
||||
#define CHOREOINPUT_H_
|
||||
#include "TembooGlobal.h"
|
||||
class ChoreoInput {
|
||||
public:
|
||||
ChoreoInput(ChoreoInput* prev, const char* name, const char* value);
|
||||
const char* getName() const {return m_name;}
|
||||
const char* getValue() const {return m_value;}
|
||||
void setValue(const char* value) {m_value = value;}
|
||||
ChoreoInput* getNext() const {return m_next;}
|
||||
|
||||
private:
|
||||
ChoreoInput* m_next;
|
||||
const char* m_name;
|
||||
const char* m_value;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "ChoreoInputExpression.h"
|
||||
|
||||
ChoreoInputExpression::ChoreoInputExpression(ChoreoInputExpression* prev, const char* name, const char* value) {
|
||||
if (prev != NULL) {
|
||||
prev->m_next = this;
|
||||
}
|
||||
m_next = NULL;
|
||||
m_name = name;
|
||||
m_value = value;
|
||||
}
|
||||
|
||||
41
arduino/libraries/Temboo/src/utility/ChoreoInputExpression.h
Normal file
41
arduino/libraries/Temboo/src/utility/ChoreoInputExpression.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOINPUTEXPRESSION_H_
|
||||
#define CHOREOINPUTEXPRESSION_H_
|
||||
|
||||
#include "TembooGlobal.h"
|
||||
class ChoreoInputExpression {
|
||||
public:
|
||||
ChoreoInputExpression(ChoreoInputExpression* prev, const char* name, const char* value);
|
||||
const char* getName() const {return m_name;}
|
||||
const char* getValue() const {return m_value;}
|
||||
void setValue(const char* value) {m_value = value;}
|
||||
ChoreoInputExpression* getNext() const {return m_next;}
|
||||
|
||||
private:
|
||||
ChoreoInputExpression* m_next;
|
||||
const char* m_name;
|
||||
const char* m_value;
|
||||
};
|
||||
|
||||
#endif //CHOREOINPUTEXPRESSION_H_
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "ChoreoInputExpressionFormatter.h"
|
||||
#include "ChoreoInputExpressionSet.h"
|
||||
|
||||
static const char TAG_INPUTS_START[] PROGMEM = "\"inputExpressions\":{";
|
||||
|
||||
ChoreoInputExpressionFormatter::ChoreoInputExpressionFormatter(const ChoreoInputExpressionSet* inputSet) {
|
||||
m_inputSet = inputSet;
|
||||
reset();
|
||||
}
|
||||
|
||||
void ChoreoInputExpressionFormatter::reset() {
|
||||
m_currentInput = NULL;
|
||||
m_nextChar = NULL;
|
||||
if (m_inputSet == NULL || m_inputSet->isEmpty()) {
|
||||
m_nextState = END;
|
||||
} else {
|
||||
m_nextState = START;
|
||||
}
|
||||
}
|
||||
|
||||
bool ChoreoInputExpressionFormatter::hasNext() {
|
||||
return m_nextState != END;
|
||||
}
|
||||
|
||||
char ChoreoInputExpressionFormatter::next() {
|
||||
char c;
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
c = readStartTagChar(TAG_INPUTS_START, INPUTS_TAG);
|
||||
break;
|
||||
|
||||
case INPUTS_TAG:
|
||||
c = readTagChar(NAME_START);
|
||||
if (m_nextState == NAME_START) {
|
||||
m_currentInput= m_inputSet->getFirstInput();
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getName();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = NAME_END;
|
||||
} else {
|
||||
m_nextState = NAME;
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME:
|
||||
c = readValueChar(NAME_END);
|
||||
break;
|
||||
|
||||
case NAME_END:
|
||||
c = '"';
|
||||
m_nextState = NAME_VALUE_SEPARATOR;
|
||||
break;
|
||||
|
||||
case NAME_VALUE_SEPARATOR:
|
||||
c = ':';
|
||||
m_nextState = VALUE_START;
|
||||
break;
|
||||
|
||||
case VALUE_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getValue();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = VALUE_END;
|
||||
} else {
|
||||
m_nextState = VALUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case VALUE:
|
||||
c = readValueChar(VALUE_END);
|
||||
break;
|
||||
|
||||
case VALUE_END:
|
||||
c = '"';
|
||||
m_currentInput = m_currentInput->getNext();
|
||||
if (m_currentInput != NULL) {
|
||||
m_nextState = NEXT_INPUT;
|
||||
} else {
|
||||
m_nextState = INPUTS_END;
|
||||
}
|
||||
break;
|
||||
case NEXT_INPUT:
|
||||
c = ',';
|
||||
m_nextChar = m_currentInput->getName();
|
||||
m_nextState = NAME_START;
|
||||
break;
|
||||
|
||||
case INPUTS_END:
|
||||
c = '}';
|
||||
m_nextState = END;
|
||||
break;
|
||||
case END:
|
||||
default:
|
||||
c = '\0';
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOINPUTEXPRESSIONFORMATTER_H_
|
||||
#define CHOREOINPUTEXPRESSIONFORMATTER_H_
|
||||
|
||||
#include "TembooGlobal.h"
|
||||
#include "BaseFormatter.h"
|
||||
#include "ChoreoInputExpressionSet.h"
|
||||
|
||||
class ChoreoInputExpressionFormatter : public BaseFormatter {
|
||||
|
||||
public:
|
||||
ChoreoInputExpressionFormatter(const ChoreoInputExpressionSet* inputSet);
|
||||
bool hasNext();
|
||||
char next();
|
||||
void reset();
|
||||
|
||||
protected:
|
||||
const ChoreoInputExpressionSet* m_inputSet;
|
||||
const ChoreoInputExpression* m_currentInput;
|
||||
|
||||
|
||||
enum State {
|
||||
START,
|
||||
INPUTS_TAG,
|
||||
NAME_START,
|
||||
NAME,
|
||||
NAME_END,
|
||||
NAME_VALUE_SEPARATOR,
|
||||
VALUE_START,
|
||||
VALUE,
|
||||
VALUE_END,
|
||||
NEXT_INPUT,
|
||||
INPUTS_END,
|
||||
END
|
||||
};
|
||||
};
|
||||
|
||||
#endif //CHOREOINPUTEXPRESSIONFORMATTER_H_
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "ChoreoInputExpressionSet.h"
|
||||
|
||||
ChoreoInputExpressionSet::ChoreoInputExpressionSet() {
|
||||
m_first = NULL;
|
||||
}
|
||||
|
||||
ChoreoInputExpressionSet::~ChoreoInputExpressionSet() {
|
||||
ChoreoInputExpression* i = m_first;
|
||||
ChoreoInputExpression* next = NULL;
|
||||
while (i != NULL) {
|
||||
next = i->getNext();
|
||||
delete i;
|
||||
i = next;
|
||||
}
|
||||
}
|
||||
|
||||
void ChoreoInputExpressionSet::put(const char* name, const char* value) {
|
||||
|
||||
// Haven't set ANY inputs yet?
|
||||
// Just create a new one.
|
||||
if (m_first == NULL) {
|
||||
m_first = new ChoreoInputExpression(NULL, name, value);
|
||||
} else {
|
||||
// Some inputs already set.
|
||||
// See if we already have this input.
|
||||
ChoreoInputExpression* last = NULL;
|
||||
ChoreoInputExpression* i = m_first;
|
||||
while(i != NULL) {
|
||||
if (strcmp(i->getName(), name) == 0) {
|
||||
// We already have an input with this name.
|
||||
// Just update the value.
|
||||
i->setValue(value);
|
||||
break;
|
||||
}
|
||||
last = i;
|
||||
i = i->getNext();
|
||||
}
|
||||
|
||||
// We don't have an input with this name
|
||||
// So we need to create a new one.
|
||||
if (i == NULL) {
|
||||
new ChoreoInputExpression(last, name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* ChoreoInputExpressionSet::get(const char* name) const {
|
||||
ChoreoInputExpression* i = m_first;
|
||||
while(i != NULL) {
|
||||
if (strcmp(i->getName(), name) == 0) {
|
||||
return i->getValue();
|
||||
}
|
||||
i = i->getNext();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOINPUTEXPRESSIONSET_H_
|
||||
#define CHOREOINPUTEXPRESSIONSET_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include "TembooGlobal.h"
|
||||
#include "ChoreoInputExpression.h"
|
||||
|
||||
class ChoreoInputExpressionSet {
|
||||
|
||||
public:
|
||||
ChoreoInputExpressionSet();
|
||||
~ChoreoInputExpressionSet();
|
||||
void put(const char* name, const char* value);
|
||||
const char* get(const char* name) const;
|
||||
bool isEmpty() const {return m_first == NULL;}
|
||||
const ChoreoInputExpression* getFirstInput() const {return m_first;}
|
||||
|
||||
protected:
|
||||
ChoreoInputExpression* m_first;
|
||||
};
|
||||
|
||||
#endif //CHOREOINPUTEXPRESSIONSET_H_
|
||||
125
arduino/libraries/Temboo/src/utility/ChoreoInputFormatter.cpp
Normal file
125
arduino/libraries/Temboo/src/utility/ChoreoInputFormatter.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "ChoreoInputFormatter.h"
|
||||
#include "ChoreoInputSet.h"
|
||||
|
||||
static const char TAG_INPUTS_START[] PROGMEM = "\"inputs\":{";
|
||||
|
||||
ChoreoInputFormatter::ChoreoInputFormatter(const ChoreoInputSet* inputSet) {
|
||||
m_inputSet = inputSet;
|
||||
reset();
|
||||
}
|
||||
|
||||
void ChoreoInputFormatter::reset() {
|
||||
m_currentInput = NULL;
|
||||
m_nextChar = NULL;
|
||||
if (m_inputSet == NULL || m_inputSet->isEmpty()) {
|
||||
m_nextState = END;
|
||||
} else {
|
||||
m_nextState = START;
|
||||
}
|
||||
}
|
||||
|
||||
bool ChoreoInputFormatter::hasNext() {
|
||||
return m_nextState != END;
|
||||
}
|
||||
|
||||
char ChoreoInputFormatter::next() {
|
||||
char c;
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
c = readStartTagChar(TAG_INPUTS_START, INPUTS_TAG);
|
||||
break;
|
||||
|
||||
case INPUTS_TAG:
|
||||
c = readTagChar(NAME_START);
|
||||
if (m_nextState == NAME_START) {
|
||||
m_currentInput= m_inputSet->getFirstInput();
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getName();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = NAME_END;
|
||||
} else {
|
||||
m_nextState = NAME;
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME:
|
||||
c = readValueChar(NAME_END);
|
||||
break;
|
||||
|
||||
case NAME_END:
|
||||
c = '"';
|
||||
m_nextState = NAME_VALUE_SEPARATOR;
|
||||
break;
|
||||
|
||||
case NAME_VALUE_SEPARATOR:
|
||||
c = ':';
|
||||
m_nextState = VALUE_START;
|
||||
break;
|
||||
|
||||
case VALUE_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getValue();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = VALUE_END;
|
||||
} else {
|
||||
m_nextState = VALUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case VALUE:
|
||||
c = readValueChar(VALUE_END);
|
||||
break;
|
||||
|
||||
case VALUE_END:
|
||||
c = '"';
|
||||
m_currentInput = m_currentInput->getNext();
|
||||
if (m_currentInput != NULL) {
|
||||
m_nextState = NEXT_INPUT;
|
||||
} else {
|
||||
m_nextState = INPUTS_END;
|
||||
}
|
||||
break;
|
||||
case NEXT_INPUT:
|
||||
c = ',';
|
||||
m_nextChar = m_currentInput->getName();
|
||||
m_nextState = NAME_START;
|
||||
break;
|
||||
|
||||
case INPUTS_END:
|
||||
c = '}';
|
||||
m_nextState = END;
|
||||
break;
|
||||
case END:
|
||||
default:
|
||||
c = '\0';
|
||||
}
|
||||
return c;
|
||||
}
|
||||
58
arduino/libraries/Temboo/src/utility/ChoreoInputFormatter.h
Normal file
58
arduino/libraries/Temboo/src/utility/ChoreoInputFormatter.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOINPUTFORMATTER_H_
|
||||
#define CHOREOINPUTFORMATTER_H_
|
||||
#include "TembooGlobal.h"
|
||||
#include "BaseFormatter.h"
|
||||
#include "ChoreoInputSet.h"
|
||||
|
||||
class ChoreoInputFormatter : public BaseFormatter {
|
||||
|
||||
public:
|
||||
ChoreoInputFormatter(const ChoreoInputSet* inputSet);
|
||||
bool hasNext();
|
||||
char next();
|
||||
void reset();
|
||||
|
||||
protected:
|
||||
const ChoreoInputSet* m_inputSet;
|
||||
const ChoreoInput* m_currentInput;
|
||||
|
||||
|
||||
enum State {
|
||||
START,
|
||||
INPUTS_TAG,
|
||||
NAME_START,
|
||||
NAME,
|
||||
NAME_END,
|
||||
NAME_VALUE_SEPARATOR,
|
||||
VALUE_START,
|
||||
VALUE,
|
||||
VALUE_END,
|
||||
NEXT_INPUT,
|
||||
INPUTS_END,
|
||||
END
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
80
arduino/libraries/Temboo/src/utility/ChoreoInputSet.cpp
Normal file
80
arduino/libraries/Temboo/src/utility/ChoreoInputSet.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "ChoreoInputSet.h"
|
||||
|
||||
ChoreoInputSet::ChoreoInputSet() {
|
||||
m_first = NULL;
|
||||
}
|
||||
|
||||
ChoreoInputSet::~ChoreoInputSet() {
|
||||
ChoreoInput* i = m_first;
|
||||
ChoreoInput* next = NULL;
|
||||
while (i != NULL) {
|
||||
next = i->getNext();
|
||||
delete i;
|
||||
i = next;
|
||||
}
|
||||
}
|
||||
|
||||
void ChoreoInputSet::put(const char* name, const char* value) {
|
||||
|
||||
// Haven't set ANY inputs yet?
|
||||
// Just create a new one.
|
||||
if (m_first == NULL) {
|
||||
m_first = new ChoreoInput(NULL, name, value);
|
||||
} else {
|
||||
// Some inputs already set.
|
||||
// See if we already have this input.
|
||||
ChoreoInput* last = NULL;
|
||||
ChoreoInput* i = m_first;
|
||||
while(i != NULL) {
|
||||
if (strcmp(i->getName(), name) == 0) {
|
||||
// We already have an input with this name.
|
||||
// Just update the value.
|
||||
i->setValue(value);
|
||||
break;
|
||||
}
|
||||
last = i;
|
||||
i = i->getNext();
|
||||
}
|
||||
|
||||
// We don't have an input with this name
|
||||
// So we need to create a new one.
|
||||
if (i == NULL) {
|
||||
new ChoreoInput(last, name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* ChoreoInputSet::get(const char* name) const {
|
||||
ChoreoInput* i = m_first;
|
||||
while(i != NULL) {
|
||||
if (strcmp(i->getName(), name) == 0) {
|
||||
return i->getValue();
|
||||
}
|
||||
i = i->getNext();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
43
arduino/libraries/Temboo/src/utility/ChoreoInputSet.h
Normal file
43
arduino/libraries/Temboo/src/utility/ChoreoInputSet.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOINPUTSET_H_
|
||||
#define CHOREOINPUTSET_H_
|
||||
#include <stddef.h>
|
||||
#include "TembooGlobal.h"
|
||||
#include "ChoreoInput.h"
|
||||
|
||||
class ChoreoInputSet {
|
||||
|
||||
public:
|
||||
ChoreoInputSet();
|
||||
~ChoreoInputSet();
|
||||
void put(const char* name, const char* value);
|
||||
const char* get(const char* name) const;
|
||||
bool isEmpty() const {return m_first == NULL;}
|
||||
const ChoreoInput* getFirstInput() const {return m_first;}
|
||||
|
||||
protected:
|
||||
ChoreoInput* m_first;
|
||||
};
|
||||
|
||||
#endif
|
||||
37
arduino/libraries/Temboo/src/utility/ChoreoOutput.cpp
Normal file
37
arduino/libraries/Temboo/src/utility/ChoreoOutput.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "ChoreoOutput.h"
|
||||
|
||||
|
||||
ChoreoOutput::ChoreoOutput(ChoreoOutput* prev, const char* name, const char* path, const char* var) {
|
||||
if (prev != NULL) {
|
||||
prev->m_next = this;
|
||||
}
|
||||
m_next = NULL;
|
||||
m_name = name;
|
||||
m_path = path;
|
||||
m_var = var;
|
||||
}
|
||||
|
||||
|
||||
44
arduino/libraries/Temboo/src/utility/ChoreoOutput.h
Normal file
44
arduino/libraries/Temboo/src/utility/ChoreoOutput.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOOUTPUT_H_
|
||||
#define CHOREOOUTPUT_H_
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
class ChoreoOutput {
|
||||
public:
|
||||
ChoreoOutput(ChoreoOutput* prev, const char* name, const char* path, const char* var);
|
||||
const char* getName() const {return m_name;}
|
||||
const char* getPath() const {return m_path;}
|
||||
const char* getVariable() const {return m_var;}
|
||||
void setPath(const char* path) {m_path = path;}
|
||||
void setVariable(const char* variable) {m_var = variable;}
|
||||
ChoreoOutput* getNext() const {return m_next;}
|
||||
|
||||
private:
|
||||
ChoreoOutput* m_next;
|
||||
const char* m_name;
|
||||
const char* m_path;
|
||||
const char* m_var;
|
||||
};
|
||||
|
||||
#endif
|
||||
181
arduino/libraries/Temboo/src/utility/ChoreoOutputFormatter.cpp
Normal file
181
arduino/libraries/Temboo/src/utility/ChoreoOutputFormatter.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "ChoreoOutputFormatter.h"
|
||||
#include "ChoreoOutputSet.h"
|
||||
|
||||
static const char TAG_OUTPUTS_START[] PROGMEM = "\"outputs\":[";
|
||||
static const char TAG_NAME[] PROGMEM = "\"name\":";
|
||||
static const char TAG_PATH[] PROGMEM = "\"path\":";
|
||||
static const char TAG_VAR[] PROGMEM = "\"variable\":";
|
||||
|
||||
|
||||
ChoreoOutputFormatter::ChoreoOutputFormatter(const ChoreoOutputSet* outputSet) {
|
||||
m_outputSet = outputSet;
|
||||
reset();
|
||||
}
|
||||
|
||||
void ChoreoOutputFormatter::reset() {
|
||||
m_currentOutput = NULL;
|
||||
m_nextChar = NULL;
|
||||
if (m_outputSet == NULL || m_outputSet->isEmpty()) {
|
||||
m_nextState = END;
|
||||
} else {
|
||||
m_nextState = START;
|
||||
}
|
||||
}
|
||||
|
||||
bool ChoreoOutputFormatter::hasNext() {
|
||||
return m_nextState != END;
|
||||
}
|
||||
|
||||
char ChoreoOutputFormatter::next() {
|
||||
char c = '\0';
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
c = readStartTagChar(TAG_OUTPUTS_START, OUTPUTS_TAG);
|
||||
break;
|
||||
|
||||
case OUTPUTS_TAG:
|
||||
c = readTagChar(OUTPUT_START);
|
||||
if (m_nextState == OUTPUT_START) {
|
||||
m_currentOutput = m_outputSet->getFirstOutput();
|
||||
}
|
||||
break;
|
||||
|
||||
case OUTPUT_START:
|
||||
c = '{';
|
||||
m_nextChar = TAG_NAME;
|
||||
m_nextState = NAME_TAG;
|
||||
break;
|
||||
|
||||
case NAME_TAG:
|
||||
c = readTagChar(NAME_START);
|
||||
break;
|
||||
|
||||
case NAME_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentOutput->getName();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = NAME_END;
|
||||
} else {
|
||||
m_nextState = NAME;
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME:
|
||||
c = readValueChar(NAME_END);
|
||||
break;
|
||||
|
||||
case NAME_END:
|
||||
c = '"';
|
||||
m_nextState = NAME_PATH_SEPARATOR;
|
||||
break;
|
||||
|
||||
case NAME_PATH_SEPARATOR:
|
||||
c = ',';
|
||||
m_nextState = PATH_TAG;
|
||||
m_nextChar = TAG_PATH;
|
||||
break;
|
||||
|
||||
case PATH_TAG:
|
||||
c = readTagChar(PATH_START);
|
||||
break;
|
||||
|
||||
case PATH_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentOutput->getPath();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = PATH_END;
|
||||
} else {
|
||||
m_nextState = PATH;
|
||||
}
|
||||
break;
|
||||
|
||||
case PATH:
|
||||
c = readValueChar(PATH_END);
|
||||
break;
|
||||
|
||||
case PATH_END:
|
||||
c = '"';
|
||||
m_nextState = PATH_VAR_SEPARATOR;
|
||||
break;
|
||||
|
||||
case PATH_VAR_SEPARATOR:
|
||||
c = ',';
|
||||
m_nextState = VAR_TAG;
|
||||
m_nextChar = TAG_VAR;
|
||||
break;
|
||||
|
||||
case VAR_TAG:
|
||||
c = readTagChar(VAR_START);
|
||||
break;
|
||||
|
||||
case VAR_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentOutput->getVariable();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = VAR_END;
|
||||
} else {
|
||||
m_nextState = VAR;
|
||||
}
|
||||
break;
|
||||
|
||||
case VAR:
|
||||
c = readValueChar(VAR_END);
|
||||
break;
|
||||
|
||||
case VAR_END:
|
||||
c = '"';
|
||||
m_nextState = OUTPUT_END;
|
||||
break;
|
||||
|
||||
case OUTPUT_END:
|
||||
c = '}';
|
||||
m_currentOutput = m_currentOutput->getNext();
|
||||
if (m_currentOutput != NULL) {
|
||||
m_nextState = NEXT_OUTPUT;
|
||||
} else {
|
||||
m_nextState = OUTPUTS_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case NEXT_OUTPUT:
|
||||
c = ',';
|
||||
m_nextChar = m_currentOutput->getName();
|
||||
m_nextState = OUTPUT_START;
|
||||
break;
|
||||
|
||||
case OUTPUTS_END:
|
||||
c = ']';
|
||||
m_nextState = END;
|
||||
break;
|
||||
case END:
|
||||
default:
|
||||
c = '\0';
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
66
arduino/libraries/Temboo/src/utility/ChoreoOutputFormatter.h
Normal file
66
arduino/libraries/Temboo/src/utility/ChoreoOutputFormatter.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOOUTPUTFORMATTER_H_
|
||||
#define CHOREOOUTPUTFORMATTER_H_
|
||||
#include "TembooGlobal.h"
|
||||
#include "BaseFormatter.h"
|
||||
#include "ChoreoOutputSet.h"
|
||||
|
||||
class ChoreoOutputFormatter : public BaseFormatter {
|
||||
|
||||
public:
|
||||
ChoreoOutputFormatter(const ChoreoOutputSet* outputSet);
|
||||
bool hasNext();
|
||||
char next();
|
||||
void reset();
|
||||
|
||||
protected:
|
||||
const ChoreoOutputSet* m_outputSet;
|
||||
const ChoreoOutput* m_currentOutput;
|
||||
|
||||
enum State {
|
||||
START,
|
||||
OUTPUTS_TAG,
|
||||
OUTPUT_START,
|
||||
NAME_TAG,
|
||||
NAME_START,
|
||||
NAME,
|
||||
NAME_END,
|
||||
NAME_PATH_SEPARATOR,
|
||||
PATH_TAG,
|
||||
PATH_START,
|
||||
PATH,
|
||||
PATH_END,
|
||||
PATH_VAR_SEPARATOR,
|
||||
VAR_TAG,
|
||||
VAR_START,
|
||||
VAR,
|
||||
VAR_END,
|
||||
OUTPUT_END,
|
||||
NEXT_OUTPUT,
|
||||
OUTPUTS_END,
|
||||
END
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
73
arduino/libraries/Temboo/src/utility/ChoreoOutputSet.cpp
Normal file
73
arduino/libraries/Temboo/src/utility/ChoreoOutputSet.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "ChoreoOutputSet.h"
|
||||
|
||||
|
||||
ChoreoOutputSet::ChoreoOutputSet() {
|
||||
m_first = NULL;
|
||||
}
|
||||
|
||||
ChoreoOutputSet::~ChoreoOutputSet() {
|
||||
ChoreoOutput* i = m_first;
|
||||
ChoreoOutput* next = NULL;
|
||||
while(i != NULL) {
|
||||
next = i->getNext();
|
||||
delete i;
|
||||
i = next;
|
||||
}
|
||||
}
|
||||
|
||||
void ChoreoOutputSet::put(const char* name, const char* path, const char* variable) {
|
||||
if (m_first == NULL) {
|
||||
m_first = new ChoreoOutput(NULL, name, path, variable);
|
||||
} else {
|
||||
ChoreoOutput* last = NULL;
|
||||
ChoreoOutput* i = m_first;
|
||||
while(i != NULL) {
|
||||
if (strcmp(i->getName(), name) == 0) {
|
||||
i->setPath(path);
|
||||
i->setVariable(variable);
|
||||
break;
|
||||
}
|
||||
last = i;
|
||||
i = i->getNext();
|
||||
}
|
||||
|
||||
if (i == NULL) {
|
||||
new ChoreoOutput(last, name, path, variable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ChoreoOutput* ChoreoOutputSet::get(const char* name) const {
|
||||
ChoreoOutput* i = m_first;
|
||||
while(i != NULL) {
|
||||
if (strcmp(i->getName(), name) == 0) {
|
||||
return i;
|
||||
}
|
||||
i = i->getNext();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
43
arduino/libraries/Temboo/src/utility/ChoreoOutputSet.h
Normal file
43
arduino/libraries/Temboo/src/utility/ChoreoOutputSet.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOOUTPUTSET_H_
|
||||
#define CHOREOOUTPUTSET_H_
|
||||
#include <stddef.h>
|
||||
#include "TembooGlobal.h"
|
||||
#include "ChoreoOutput.h"
|
||||
|
||||
class ChoreoOutputSet {
|
||||
|
||||
public:
|
||||
ChoreoOutputSet();
|
||||
~ChoreoOutputSet();
|
||||
void put(const char* name, const char* path, const char* variable);
|
||||
const ChoreoOutput* get(const char* name) const;
|
||||
bool isEmpty() const {return m_first == NULL;}
|
||||
const ChoreoOutput* getFirstOutput() const {return m_first;}
|
||||
|
||||
protected:
|
||||
ChoreoOutput* m_first;
|
||||
};
|
||||
|
||||
#endif
|
||||
23
arduino/libraries/Temboo/src/utility/ChoreoPreset.cpp
Normal file
23
arduino/libraries/Temboo/src/utility/ChoreoPreset.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include "ChoreoPreset.h"
|
||||
40
arduino/libraries/Temboo/src/utility/ChoreoPreset.h
Normal file
40
arduino/libraries/Temboo/src/utility/ChoreoPreset.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOPRESET_H_
|
||||
#define CHOREOPRESET_H_
|
||||
#include <stddef.h>
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
class ChoreoPreset {
|
||||
public:
|
||||
ChoreoPreset() {m_name = NULL;}
|
||||
ChoreoPreset(const char* name) {put(name);}
|
||||
const char* getName() const {return m_name;}
|
||||
void put(const char* name) {m_name = name;}
|
||||
bool isEmpty() const {return m_name == NULL || *m_name == '\0';}
|
||||
|
||||
private:
|
||||
const char* m_name;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "ChoreoPresetFormatter.h"
|
||||
#include "ChoreoPreset.h"
|
||||
|
||||
static const char TAG_PRESET[] PROGMEM = "\"preset\":";
|
||||
|
||||
ChoreoPresetFormatter::ChoreoPresetFormatter(const ChoreoPreset* preset) {
|
||||
m_preset = preset;
|
||||
reset();
|
||||
}
|
||||
|
||||
void ChoreoPresetFormatter::reset() {
|
||||
m_nextChar = NULL;
|
||||
if (m_preset == NULL || m_preset->isEmpty()) {
|
||||
m_nextState = END;
|
||||
} else {
|
||||
m_nextState = START;
|
||||
}
|
||||
}
|
||||
|
||||
bool ChoreoPresetFormatter::hasNext() {
|
||||
return m_nextState != END;
|
||||
}
|
||||
|
||||
char ChoreoPresetFormatter::next() {
|
||||
char c = '\0';
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
c = readStartTagChar(TAG_PRESET, PRESET_TAG);
|
||||
break;
|
||||
|
||||
case PRESET_TAG:
|
||||
c = readTagChar(NAME_START);
|
||||
break;
|
||||
|
||||
case NAME_START:
|
||||
c = '"';
|
||||
m_nextChar = m_preset->getName();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = NAME_END;
|
||||
} else {
|
||||
m_nextState = NAME;
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME:
|
||||
c = readValueChar(NAME_END);
|
||||
break;
|
||||
|
||||
case NAME_END:
|
||||
c = '"';
|
||||
m_nextState = END;
|
||||
break;
|
||||
|
||||
case END:
|
||||
default:
|
||||
c = '\0';
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
50
arduino/libraries/Temboo/src/utility/ChoreoPresetFormatter.h
Normal file
50
arduino/libraries/Temboo/src/utility/ChoreoPresetFormatter.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOPROFILEFORMATTER_H_
|
||||
#define CHOREOPROFILEFORMATTER_H_
|
||||
#include "TembooGlobal.h"
|
||||
#include "BaseFormatter.h"
|
||||
#include "ChoreoPreset.h"
|
||||
|
||||
class ChoreoPresetFormatter : public BaseFormatter {
|
||||
|
||||
public:
|
||||
ChoreoPresetFormatter(const ChoreoPreset* preset);
|
||||
bool hasNext();
|
||||
char next();
|
||||
void reset();
|
||||
|
||||
protected:
|
||||
const ChoreoPreset* m_preset;
|
||||
|
||||
enum State {
|
||||
START,
|
||||
PRESET_TAG,
|
||||
NAME_START,
|
||||
NAME,
|
||||
NAME_END,
|
||||
END
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
41
arduino/libraries/Temboo/src/utility/ChoreoSensorInput.cpp
Normal file
41
arduino/libraries/Temboo/src/utility/ChoreoSensorInput.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "ChoreoSensorInput.h"
|
||||
|
||||
ChoreoSensorInput::ChoreoSensorInput(ChoreoSensorInput* prev, const char* name, int value, const char* sensorConversion, const char* rawLow, const char* rawHigh, const char* scaleLow, const char* scaleHigh, const char* calibration) {
|
||||
if (prev != NULL) {
|
||||
prev->m_next = this;
|
||||
}
|
||||
m_next = NULL;
|
||||
m_name = name;
|
||||
snprintf(m_value, TEMBOO_SENSOR_INPUT_ARRAY_SIZE, "%i",value);
|
||||
m_sensorConversion = sensorConversion;
|
||||
m_rawLow = rawLow;
|
||||
m_rawHigh = rawHigh;
|
||||
m_scaleLow = scaleLow;
|
||||
m_scaleHigh = scaleHigh;
|
||||
m_calibration = calibration;
|
||||
}
|
||||
|
||||
|
||||
63
arduino/libraries/Temboo/src/utility/ChoreoSensorInput.h
Normal file
63
arduino/libraries/Temboo/src/utility/ChoreoSensorInput.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOSENSORINPUT_H_
|
||||
#define CHOREOSENSORINPUT_H_
|
||||
|
||||
#include "TembooGlobal.h"
|
||||
#define TEMBOO_SENSOR_INPUT_ARRAY_SIZE 8
|
||||
|
||||
class ChoreoSensorInput {
|
||||
public:
|
||||
|
||||
ChoreoSensorInput(ChoreoSensorInput* prev, const char* name, int value, const char* sensorConversion, const char* rawLow, const char* rawHigh, const char* scaleLow, const char* scaleHigh, const char* calibration);
|
||||
|
||||
const char* getName() const {return m_name;}
|
||||
const char* getValue() const {return m_value;}
|
||||
const char* getConversion() const {return m_sensorConversion;}
|
||||
const char* getRawLow() const {return m_rawLow;}
|
||||
const char* getRawHigh() const {return m_rawHigh;}
|
||||
const char* getScaleLow() const {return m_scaleLow;}
|
||||
const char* getScaleHigh() const {return m_scaleHigh;}
|
||||
const char* getCalibration() const {return m_calibration;}
|
||||
void setValue(int value) {snprintf(m_value, TEMBOO_SENSOR_INPUT_ARRAY_SIZE, "%i",value);}
|
||||
void setConversion(const char* conversion) {m_sensorConversion = conversion;}
|
||||
void setRawLow(const char* value) {m_rawLow = value;}
|
||||
void setRawHigh(const char* value) {m_rawHigh = value;}
|
||||
void setScaleLow(const char* value) {m_scaleLow = value;}
|
||||
void setScaleHigh(const char* value) {m_scaleHigh = value;}
|
||||
void setCalibration(const char* value) {m_calibration = value;}
|
||||
ChoreoSensorInput* getNext() const {return m_next;}
|
||||
|
||||
private:
|
||||
ChoreoSensorInput* m_next;
|
||||
const char* m_name;
|
||||
char m_value[TEMBOO_SENSOR_INPUT_ARRAY_SIZE];
|
||||
const char* m_sensorConversion;
|
||||
const char* m_rawLow;
|
||||
const char* m_rawHigh;
|
||||
const char* m_scaleLow;
|
||||
const char* m_scaleHigh;
|
||||
const char* m_calibration;
|
||||
};
|
||||
|
||||
#endif //CHOREOSENSORINPUT_H_
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "ChoreoSensorInputFormatter.h"
|
||||
#include "ChoreoSensorInputSet.h"
|
||||
|
||||
static const char TAG_INPUTS_START[] PROGMEM = "\"sensorInputs\":{";
|
||||
static const char TAG_SENSOR_NAME_SEPERATOR[] PROGMEM = "\":{";
|
||||
static const char TAG_VALUE_START[] PROGMEM = "\"value\":";
|
||||
static const char TAG_CALIBRATION_START[] PROGMEM = ",\"calibration\":";
|
||||
static const char TAG_RAW_LOW_START[] PROGMEM = ",\"rawLow\":";
|
||||
static const char TAG_RAW_HIGH_START[] PROGMEM = ",\"rawHigh\":";
|
||||
static const char TAG_SCALE_LOW_START[] PROGMEM = ",\"scaleLow\":";
|
||||
static const char TAG_SCALE_HIGH_START[] PROGMEM = ",\"scaleHigh\":";
|
||||
static const char TAG_CONVERSION_START[] PROGMEM = ",\"conversion\":";
|
||||
|
||||
ChoreoSensorInputFormatter::ChoreoSensorInputFormatter(const ChoreoSensorInputSet* inputSet) {
|
||||
m_inputSet = inputSet;
|
||||
reset();
|
||||
}
|
||||
|
||||
void ChoreoSensorInputFormatter::reset() {
|
||||
m_currentInput = NULL;
|
||||
m_nextChar = NULL;
|
||||
if (m_inputSet == NULL || m_inputSet->isEmpty()) {
|
||||
m_nextState = END;
|
||||
} else {
|
||||
m_nextState = START;
|
||||
}
|
||||
}
|
||||
|
||||
bool ChoreoSensorInputFormatter::hasNext() {
|
||||
return m_nextState != END;
|
||||
}
|
||||
|
||||
char ChoreoSensorInputFormatter::next() {
|
||||
char c;
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
c = readStartTagChar(TAG_INPUTS_START, SENSOR_INPUT_TAG);
|
||||
break;
|
||||
|
||||
case SENSOR_INPUT_TAG:
|
||||
c = readTagChar(SENSOR_NAME_START);
|
||||
if (m_nextState == SENSOR_NAME_START) {
|
||||
m_currentInput= m_inputSet->getFirstInput();
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_NAME_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getName();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = SENSOR_NAME_END;
|
||||
} else {
|
||||
m_nextState = SENSOR_NAME;
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_NAME:
|
||||
c = readValueChar(SENSOR_NAME_END);
|
||||
break;
|
||||
|
||||
case SENSOR_NAME_END:
|
||||
c = readStartTagChar(TAG_SENSOR_NAME_SEPERATOR, SENSOR_NAME_SEPERATOR);
|
||||
break;
|
||||
|
||||
case SENSOR_NAME_SEPERATOR:
|
||||
c = readTagChar(VALUE_TAG);
|
||||
if (m_nextState == VALUE_TAG) {
|
||||
m_nextChar= TAG_VALUE_START;
|
||||
}
|
||||
break;
|
||||
|
||||
case VALUE_TAG:
|
||||
c = readTagChar(VALUE_START);
|
||||
break;
|
||||
|
||||
case VALUE_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getValue();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = VALUE_END;
|
||||
} else {
|
||||
m_nextState = VALUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case VALUE:
|
||||
c = readValueChar(VALUE_END);
|
||||
break;
|
||||
|
||||
case VALUE_END:
|
||||
c = '"';
|
||||
// decide how the data set should be set up, with just the value, with
|
||||
// a conversion, or with the user defined scale
|
||||
if (m_currentInput->getCalibration() != NULL) {
|
||||
m_nextChar = TAG_CALIBRATION_START;
|
||||
m_nextState = CALIBRATION_TAG;
|
||||
} else {
|
||||
if (m_currentInput->getConversion() != NULL) {
|
||||
// use conversion
|
||||
m_nextState = CONVERSION_TAG;
|
||||
m_nextChar = TAG_CONVERSION_START;
|
||||
} else if (m_currentInput->getScaleHigh() != m_currentInput->getScaleLow()) {
|
||||
// use user defined scale
|
||||
m_nextState = RAW_LOW_TAG;
|
||||
m_nextChar = TAG_RAW_LOW_START;
|
||||
} else {
|
||||
// just the value is needed
|
||||
m_nextState = DATA_SET_END;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CALIBRATION_TAG:
|
||||
c = readTagChar(CALIBRATION_START);
|
||||
break;
|
||||
|
||||
case CALIBRATION_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getCalibration();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = CALIBRATION_END;
|
||||
} else {
|
||||
m_nextState = CALIBRATION;
|
||||
}
|
||||
break;
|
||||
|
||||
case CALIBRATION:
|
||||
c = readValueChar(CALIBRATION_END);
|
||||
break;
|
||||
|
||||
case CALIBRATION_END:
|
||||
c = '"';
|
||||
if (m_currentInput->getConversion() != NULL) {
|
||||
// use conversion
|
||||
m_nextState = CONVERSION_TAG;
|
||||
m_nextChar = TAG_CONVERSION_START;
|
||||
} else if (m_currentInput->getScaleHigh() != m_currentInput->getScaleLow()) {
|
||||
// use user defined scale
|
||||
m_nextState = RAW_LOW_TAG;
|
||||
m_nextChar = TAG_RAW_LOW_START;
|
||||
} else {
|
||||
// just the value is needed
|
||||
m_nextState = DATA_SET_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case CONVERSION_TAG:
|
||||
c = readTagChar(CONVERSION_START);
|
||||
break;
|
||||
|
||||
case CONVERSION_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getConversion();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = CONVERSION_END;
|
||||
} else {
|
||||
m_nextState = CONVERSION;
|
||||
}
|
||||
break;
|
||||
|
||||
case CONVERSION:
|
||||
c = readValueChar(CONVERSION_END);
|
||||
break;
|
||||
|
||||
case CONVERSION_END:
|
||||
c = '"';
|
||||
m_nextState = DATA_SET_END;
|
||||
break;
|
||||
|
||||
case RAW_LOW_TAG:
|
||||
c = readTagChar(RAW_LOW_START);
|
||||
break;
|
||||
|
||||
case RAW_LOW_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getRawLow();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = RAW_LOW_END;
|
||||
} else {
|
||||
m_nextState = RAW_LOW;
|
||||
}
|
||||
break;
|
||||
|
||||
case RAW_LOW:
|
||||
c = readValueChar(RAW_LOW_END);
|
||||
break;
|
||||
|
||||
case RAW_LOW_END:
|
||||
c = '"';
|
||||
m_nextState = RAW_HIGH_TAG;
|
||||
m_nextChar = TAG_RAW_HIGH_START;
|
||||
break;
|
||||
|
||||
case RAW_HIGH_TAG:
|
||||
c = readTagChar(RAW_HIGH_START);
|
||||
break;
|
||||
|
||||
case RAW_HIGH_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getRawHigh();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = RAW_HIGH_END;
|
||||
} else {
|
||||
m_nextState = RAW_HIGH;
|
||||
}
|
||||
break;
|
||||
|
||||
case RAW_HIGH:
|
||||
c = readValueChar(RAW_HIGH_END);
|
||||
break;
|
||||
|
||||
case RAW_HIGH_END:
|
||||
c = '"';
|
||||
m_nextState = SCALE_LOW_TAG;
|
||||
m_nextChar = TAG_SCALE_LOW_START;
|
||||
break;
|
||||
|
||||
case SCALE_LOW_TAG:
|
||||
c = readTagChar(SCALE_LOW_START);
|
||||
break;
|
||||
|
||||
case SCALE_LOW_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getScaleLow();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = SCALE_LOW_END;
|
||||
} else {
|
||||
m_nextState = SCALE_LOW;
|
||||
}
|
||||
break;
|
||||
|
||||
case SCALE_LOW:
|
||||
c = readValueChar(SCALE_LOW_END);
|
||||
break;
|
||||
|
||||
case SCALE_LOW_END:
|
||||
c = '"';
|
||||
m_nextState = SCALE_HIGH_TAG;
|
||||
m_nextChar = TAG_SCALE_HIGH_START;
|
||||
break;
|
||||
|
||||
case SCALE_HIGH_TAG:
|
||||
c = readTagChar(SCALE_HIGH_START);
|
||||
break;
|
||||
|
||||
case SCALE_HIGH_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getScaleHigh();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = SCALE_HIGH_END;
|
||||
} else {
|
||||
m_nextState = SCALE_HIGH;
|
||||
}
|
||||
break;
|
||||
|
||||
case SCALE_HIGH:
|
||||
c = readValueChar(SCALE_HIGH_END);
|
||||
break;
|
||||
|
||||
case SCALE_HIGH_END:
|
||||
c = '"';
|
||||
m_nextState = DATA_SET_END;
|
||||
break;
|
||||
|
||||
case DATA_SET_END:
|
||||
c = '}';
|
||||
m_currentInput = m_currentInput->getNext();
|
||||
if (NULL != m_currentInput) {
|
||||
m_nextState = NEXT_INPUT;
|
||||
} else {
|
||||
m_nextState = INPUTS_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case NEXT_INPUT:
|
||||
c = ',';
|
||||
m_nextChar = m_currentInput->getName();
|
||||
m_nextState = SENSOR_NAME_START;
|
||||
break;
|
||||
|
||||
case INPUTS_END:
|
||||
c = '}';
|
||||
m_nextState = END;
|
||||
break;
|
||||
|
||||
case END:
|
||||
default:
|
||||
c = '\0';
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOSENSORINPUTFORMATTER_H_
|
||||
#define CHOREOSENSORINPUTFORMATTER_H_
|
||||
|
||||
#include "TembooGlobal.h"
|
||||
#include "BaseFormatter.h"
|
||||
#include "ChoreoSensorInputSet.h"
|
||||
|
||||
class ChoreoSensorInputFormatter : public BaseFormatter {
|
||||
|
||||
public:
|
||||
ChoreoSensorInputFormatter(const ChoreoSensorInputSet* inputSet);
|
||||
bool hasNext();
|
||||
char next();
|
||||
void reset();
|
||||
|
||||
protected:
|
||||
const ChoreoSensorInputSet* m_inputSet;
|
||||
const ChoreoSensorInput* m_currentInput;
|
||||
|
||||
|
||||
enum State {
|
||||
// This is added to fix a compilation bug for the mkr1000
|
||||
START = 256,
|
||||
SENSOR_INPUT_TAG,
|
||||
SENSOR_NAME_START,
|
||||
SENSOR_NAME,
|
||||
SENSOR_NAME_END,
|
||||
SENSOR_NAME_SEPERATOR,
|
||||
VALUE_TAG,
|
||||
VALUE_START,
|
||||
VALUE,
|
||||
VALUE_END,
|
||||
CALIBRATION_TAG,
|
||||
CALIBRATION_START,
|
||||
CALIBRATION,
|
||||
CALIBRATION_END,
|
||||
CONVERSION_TAG,
|
||||
CONVERSION_START,
|
||||
CONVERSION,
|
||||
CONVERSION_END,
|
||||
RAW_LOW_TAG,
|
||||
RAW_LOW_START,
|
||||
RAW_LOW,
|
||||
RAW_LOW_END,
|
||||
RAW_HIGH_TAG,
|
||||
RAW_HIGH_START,
|
||||
RAW_HIGH,
|
||||
RAW_HIGH_END,
|
||||
SCALE_LOW_TAG,
|
||||
SCALE_LOW_START,
|
||||
SCALE_LOW,
|
||||
SCALE_LOW_END,
|
||||
SCALE_HIGH_TAG,
|
||||
SCALE_HIGH_START,
|
||||
SCALE_HIGH,
|
||||
SCALE_HIGH_END,
|
||||
DATA_SET_END,
|
||||
NEXT_INPUT,
|
||||
INPUTS_END,
|
||||
END
|
||||
};
|
||||
};
|
||||
|
||||
#endif //CHOREOSENSORINPUTFORMATTER_H_
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "ChoreoSensorInputSet.h"
|
||||
|
||||
ChoreoSensorInputSet::ChoreoSensorInputSet() {
|
||||
m_first = NULL;
|
||||
}
|
||||
|
||||
ChoreoSensorInputSet::~ChoreoSensorInputSet() {
|
||||
ChoreoSensorInput* i = m_first;
|
||||
ChoreoSensorInput* next = NULL;
|
||||
while (i != NULL) {
|
||||
next = i->getNext();
|
||||
delete i;
|
||||
i = next;
|
||||
}
|
||||
}
|
||||
|
||||
void ChoreoSensorInputSet::put(const char* name, int value, const char* sensorConversion, const char* rawLow, const char* rawHigh, const char* scaleLow, const char* scaleHigh, const char* calibration) {
|
||||
|
||||
// Haven't set ANY inputs yet?
|
||||
// Just create a new one.
|
||||
if (m_first == NULL) {
|
||||
m_first = new ChoreoSensorInput(NULL, name, value, sensorConversion, rawLow, rawHigh, scaleLow, scaleHigh, calibration);
|
||||
} else {
|
||||
// Some inputs already set.
|
||||
// See if we already have this input.
|
||||
ChoreoSensorInput* last = NULL;
|
||||
ChoreoSensorInput* i = m_first;
|
||||
while(i != NULL) {
|
||||
if (strcmp(i->getName(), name) == 0) {
|
||||
// We already have an input with this name.
|
||||
// Just update the value.
|
||||
i->setValue(value);
|
||||
i->setConversion(sensorConversion);
|
||||
i->setRawLow(rawLow);
|
||||
i->setRawHigh(rawHigh);
|
||||
i->setScaleLow(scaleLow);
|
||||
i->setScaleHigh(scaleHigh);
|
||||
i->setCalibration(calibration);
|
||||
break;
|
||||
}
|
||||
last = i;
|
||||
i = i->getNext();
|
||||
}
|
||||
|
||||
// We don't have an input with this name
|
||||
// So we need to create a new one.
|
||||
if (i == NULL) {
|
||||
new ChoreoSensorInput(last, name, value, sensorConversion, rawLow, rawHigh, scaleLow, scaleHigh, calibration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* ChoreoSensorInputSet::get(const char* name) const {
|
||||
ChoreoSensorInput* i = m_first;
|
||||
while(i != NULL) {
|
||||
if (strcmp(i->getName(), name) == 0) {
|
||||
return i->getValue();
|
||||
}
|
||||
i = i->getNext();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
44
arduino/libraries/Temboo/src/utility/ChoreoSensorInputSet.h
Normal file
44
arduino/libraries/Temboo/src/utility/ChoreoSensorInputSet.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOSENSORINPUTSET_H_
|
||||
#define CHOREOSENSORINPUTSET_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include "TembooGlobal.h"
|
||||
#include "ChoreoSensorInput.h"
|
||||
|
||||
class ChoreoSensorInputSet {
|
||||
|
||||
public:
|
||||
ChoreoSensorInputSet();
|
||||
~ChoreoSensorInputSet();
|
||||
void put(const char* name, int value, const char* sensorConversion, const char* rawLow, const char* rawHigh, const char* scaleLow, const char* scaleHigh, const char* calibration);
|
||||
const char* get(const char* name) const;
|
||||
bool isEmpty() const {return m_first == NULL;}
|
||||
const ChoreoSensorInput* getFirstInput() const {return m_first;}
|
||||
|
||||
protected:
|
||||
ChoreoSensorInput* m_first;
|
||||
};
|
||||
|
||||
#endif //CHOREOSENSORINPUTSET_H_
|
||||
400
arduino/libraries/Temboo/src/utility/CoapMessageLayer.cpp
Normal file
400
arduino/libraries/Temboo/src/utility/CoapMessageLayer.cpp
Normal file
@@ -0,0 +1,400 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include "utility/CoapMessageLayer.h"
|
||||
|
||||
CoapMessageLayer::CoapMessageLayer(uint8_t* rxBuffer, uint16_t rxLen, TembooCoAPIPStack& ipStack) :
|
||||
m_rxBuffer(rxBuffer),
|
||||
m_rxLen(rxLen),
|
||||
m_ipStack(ipStack),
|
||||
m_state(STATE_CLOSED),
|
||||
m_prevState(STATE_CLOSED),
|
||||
m_retransmitCount(0),
|
||||
m_lastResult(NO_ERROR),
|
||||
m_msgID(0) {
|
||||
}
|
||||
|
||||
|
||||
CoapMessageLayer::Result CoapMessageLayer::rejectMsg(CoapMsg& msg) {
|
||||
return rejectMsg(msg, m_ipStack.getRemoteAddress(), m_ipStack.getRemotePort());
|
||||
}
|
||||
|
||||
CoapMessageLayer::Result CoapMessageLayer::rejectMsg(CoapMsg& msg, IPAddress addr, uint16_t port) {
|
||||
msg.convertToReset();
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Sending RST");
|
||||
if (m_ipStack.sendDatagram(addr, port, msg.getMsgBytes(), msg.getMsgLen())) {
|
||||
m_lastResult = ERROR_SENDING_PACKET;
|
||||
} else {
|
||||
m_lastResult = NO_ERROR;
|
||||
if (STATE_ACK_PENDING == m_state) {
|
||||
// go back to previous state since the
|
||||
// message recv'd wasn't what we're expecting
|
||||
m_state = m_prevState;
|
||||
}
|
||||
}
|
||||
return m_lastResult;
|
||||
}
|
||||
|
||||
|
||||
CoapMessageLayer::Result CoapMessageLayer::acceptMsg(CoapMsg& msg) {
|
||||
return acceptMsg(msg, m_ipStack.getRemoteAddress(), m_ipStack.getRemotePort());
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMessageLayer::Result CoapMessageLayer::acceptMsg(CoapMsg& msg, IPAddress addr, uint16_t port) {
|
||||
if (m_state != STATE_ACK_PENDING) {
|
||||
m_lastResult = ERROR_IMPROPER_STATE;
|
||||
} else {
|
||||
msg.convertToEmptyAck();
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Sending ACK");
|
||||
if (m_ipStack.sendDatagram(addr, port, msg.getMsgBytes(), msg.getMsgLen())) {
|
||||
m_lastResult = ERROR_SENDING_PACKET;
|
||||
} else {
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = NO_ERROR;
|
||||
}
|
||||
}
|
||||
return m_lastResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMessageLayer::Result CoapMessageLayer::reliableSend(CoapMsg& msg, IPAddress destAddr, uint16_t destPort) {
|
||||
|
||||
if (m_state != STATE_CLOSED) {
|
||||
m_lastResult = ERROR_IMPROPER_STATE;
|
||||
return m_lastResult;
|
||||
}
|
||||
|
||||
m_rxByteCount = 0;
|
||||
m_msgID = msg.getId();
|
||||
m_msgBytes = msg.getMsgBytes();
|
||||
m_msgLen = msg.getMsgLen();
|
||||
m_destAddr = destAddr;
|
||||
m_destPort = destPort;
|
||||
msg.setType(CoapMsg::COAP_CONFIRMABLE);
|
||||
m_retransmitCount = 0;
|
||||
m_retransmitTimeoutMillis = random(ACK_TIMEOUT, MAX_ACK_TIMEOUT);
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Sending message");
|
||||
if (m_ipStack.sendDatagram(m_destAddr, m_destPort, msg.getMsgBytes(), msg.getMsgLen())) {
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = ERROR_SENDING_PACKET;
|
||||
} else {
|
||||
m_txSpanTimer.start(MAX_TRANSMIT_WAIT);
|
||||
m_retransmitTimer.start(m_retransmitTimeoutMillis);
|
||||
m_state = STATE_RELIABLE_TX;
|
||||
m_lastResult = NO_ERROR;
|
||||
}
|
||||
|
||||
return m_lastResult;
|
||||
}
|
||||
|
||||
|
||||
CoapMessageLayer::Result CoapMessageLayer::cancelReliableSend() {
|
||||
if (m_state != STATE_RELIABLE_TX) {
|
||||
m_lastResult = ERROR_IMPROPER_STATE;
|
||||
return m_lastResult;
|
||||
}
|
||||
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = NO_ERROR;
|
||||
return m_lastResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMessageLayer::Result CoapMessageLayer::loop() {
|
||||
|
||||
m_lastResult = NO_ERROR;
|
||||
|
||||
switch(m_state) {
|
||||
case STATE_RELIABLE_TX:
|
||||
// We have sent a CON request.
|
||||
// We're expecting an ACK or a response.
|
||||
|
||||
// See if it's time to give up all hope of getting an ACK.
|
||||
if (m_txSpanTimer.expired()) {
|
||||
m_lastResult = ERROR_TX_SPAN_TIME_EXCEEDED;
|
||||
m_state = STATE_CLOSED;
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("ACK not received within timeout");
|
||||
break;
|
||||
}
|
||||
|
||||
// See if any messages have come in.
|
||||
if (m_ipStack.recvDatagram(m_rxBuffer, m_rxLen, m_rxByteCount)) {
|
||||
m_lastResult = ERROR_RECEIVING_PACKET;
|
||||
m_state = STATE_CLOSED;
|
||||
break;
|
||||
}
|
||||
|
||||
// We've received something. See if it's relevant.
|
||||
if (m_rxByteCount > 0) {
|
||||
CoapMsg msg(m_rxBuffer, m_rxLen, m_rxByteCount);
|
||||
|
||||
// Make sure the message is valid
|
||||
if (!msg.isValid()) {
|
||||
rejectMsg(msg);
|
||||
|
||||
// We're only interested in messages coming from the host
|
||||
// we sent the original request to.
|
||||
} else if (m_ipStack.getRemoteAddress() == m_destAddr) {
|
||||
switch (msg.getType()) {
|
||||
case CoapMsg::COAP_ACK:
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("ACK Received");
|
||||
// Is it ACK'ing the last request we sent?
|
||||
if (msg.getId() == m_msgID) {
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = ACK_RECEIVED;
|
||||
} else {
|
||||
// if not, just ignore it.
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("MID did not match");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_RESET:
|
||||
// Is it rejecting the last request we sent?
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("RST Received");
|
||||
if (msg.getId() == m_msgID) {
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = RESET_RECEIVED;
|
||||
} else {
|
||||
// if not, just ignore it.
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("MID did not match");
|
||||
}
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_CONFIRMABLE:
|
||||
// It COULD be the response to our request, or
|
||||
// just some unexpected message.
|
||||
// We'll let the upper layers decide.
|
||||
m_prevState = m_state;
|
||||
m_state = STATE_ACK_PENDING;
|
||||
m_lastResult = CON_RECEIVED;
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("CON Received");
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_NON_CONFIRMABLE:
|
||||
// It COULD be the response to our request, or
|
||||
// just some unexpected message.
|
||||
// We'll let the upper layers decide.
|
||||
|
||||
// That's what Kovatsch et al. show in their FSM.
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = NON_RECEIVED;
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("NON Received");
|
||||
break;
|
||||
}
|
||||
if (msg.getPayloadLen() > 0) {
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Payload data:");
|
||||
uint8_t *payload = msg.getPayload();
|
||||
uint16_t len = msg.getPayloadLen();
|
||||
for (uint16_t i = 0; i < len; i++) {
|
||||
TEMBOO_TRACE((char)payload[i]);
|
||||
}
|
||||
TEMBOO_TRACELN();
|
||||
}
|
||||
} else if (msg.getType() == CoapMsg::COAP_CONFIRMABLE) {
|
||||
// It's not from the host we sent the request to, but
|
||||
// the sending host expects a reply, so explicitly reject it.
|
||||
rejectMsg(msg);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Nothing was received. See if it's time to retransmit.
|
||||
if (m_retransmitTimer.expired()) {
|
||||
if (m_retransmitCount >= MAX_RETRANSMIT) {
|
||||
// We've retried enough. Give up.
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Maximum retransmit reached");
|
||||
m_lastResult = ERROR_RETRANSMIT_COUNT_EXCEEDED;
|
||||
m_state = STATE_CLOSED;
|
||||
} else {
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Retransmit message");
|
||||
m_retransmitCount++;
|
||||
m_retransmitTimeoutMillis *= 2;
|
||||
if (m_ipStack.sendDatagram(m_destAddr, m_destPort, m_msgBytes, m_msgLen)) {
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = ERROR_SENDING_PACKET;
|
||||
} else {
|
||||
m_retransmitTimer.start(m_retransmitTimeoutMillis);
|
||||
m_state = STATE_RELIABLE_TX;
|
||||
m_lastResult = NO_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_ACK_PENDING:
|
||||
// Nothing to do here but wait for
|
||||
// the higher layer to accept or reject.
|
||||
break;
|
||||
|
||||
case STATE_WAITING_FOR_CON:
|
||||
// See if any messages have come in.
|
||||
if (m_ipStack.recvDatagram(m_rxBuffer, m_rxLen, m_rxByteCount)) {
|
||||
m_lastResult = ERROR_RECEIVING_PACKET;
|
||||
m_state = STATE_CLOSED;
|
||||
break;
|
||||
}
|
||||
|
||||
// We've received something. See if it's relevant.
|
||||
if (m_rxByteCount > 0) {
|
||||
CoapMsg msg(m_rxBuffer, m_rxLen, m_rxByteCount);
|
||||
|
||||
// Make sure the message is valid
|
||||
if (!msg.isValid()) {
|
||||
rejectMsg(msg);
|
||||
|
||||
// We're only interested in messages coming from the host
|
||||
// we sent the original request to.
|
||||
} else if (m_ipStack.getRemoteAddress() == m_destAddr) {
|
||||
switch (msg.getType()) {
|
||||
case CoapMsg::COAP_ACK:
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("ACK Received");
|
||||
// Is it ACK'ing the last request we sent?
|
||||
if (msg.getId() == m_msgID) {
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = ACK_RECEIVED;
|
||||
} else {
|
||||
// if not, just ignore it.
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("MID did not match");
|
||||
}
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_RESET:
|
||||
// Is it rejecting the last request we sent?
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("RST Received");
|
||||
if (msg.getId() == m_msgID) {
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = RESET_RECEIVED;
|
||||
} else {
|
||||
// if not, just ignore it.
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("MID did not match");
|
||||
}
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_CONFIRMABLE:
|
||||
// It COULD be the response to our request, or
|
||||
// just some unexpected message.
|
||||
// We'll let the upper layers decide.
|
||||
m_prevState = m_state;
|
||||
m_state = STATE_ACK_PENDING;
|
||||
m_lastResult = CON_RECEIVED;
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("CON Received");
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_NON_CONFIRMABLE:
|
||||
// It COULD be the response to our request, or
|
||||
// just some unexpected message.
|
||||
// We'll let the upper layers decide.
|
||||
|
||||
// That's what Kovatsch et al. show in their FSM.
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = NON_RECEIVED;
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("NON Received");
|
||||
break;
|
||||
}
|
||||
if (msg.getPayloadLen() > 0) {
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Payload data:");
|
||||
uint8_t *payload = msg.getPayload();
|
||||
uint16_t len = msg.getPayloadLen();
|
||||
for (uint16_t i = 0; i < len; i++) {
|
||||
TEMBOO_TRACE((char)payload[i]);
|
||||
}
|
||||
TEMBOO_TRACELN();
|
||||
}
|
||||
} else if (msg.getType() == CoapMsg::COAP_CONFIRMABLE) {
|
||||
// It's not from the host we sent the request to, but
|
||||
// the sending host expects a reply, so explicitly reject it.
|
||||
rejectMsg(msg);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case STATE_CLOSED:
|
||||
|
||||
// We haven't sent anything that we're expecting an answer to.
|
||||
// We haven't received anything that requires an answer.
|
||||
// Just pump the receiver.
|
||||
if (m_ipStack.recvDatagram(m_rxBuffer, m_rxLen, m_rxByteCount)) {
|
||||
m_lastResult = ERROR_RECEIVING_PACKET;
|
||||
break;
|
||||
}
|
||||
|
||||
// If we received something, handle it.
|
||||
if (m_rxByteCount > 0) {
|
||||
CoapMsg msg(m_rxBuffer, m_rxLen, m_rxByteCount);
|
||||
switch (msg.getType()) {
|
||||
case CoapMsg::COAP_ACK:
|
||||
// Not expecting any ACKS, ignore it.
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_RESET:
|
||||
// Haven't sent any CONs and we don't send NONs, ignore it.
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_CONFIRMABLE:
|
||||
// Let the higher layers deal with this.
|
||||
m_prevState = m_state;
|
||||
m_state = STATE_ACK_PENDING;
|
||||
m_lastResult = CON_RECEIVED;
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_NON_CONFIRMABLE:
|
||||
// Let the higher layers deal with this.
|
||||
m_lastResult = NON_RECEIVED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
return m_lastResult;
|
||||
}
|
||||
|
||||
128
arduino/libraries/Temboo/src/utility/CoapMessageLayer.h
Normal file
128
arduino/libraries/Temboo/src/utility/CoapMessageLayer.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef COAPMESSAGELAYER_H_
|
||||
#define COAPMESSAGELAYER_H_
|
||||
|
||||
#include "TembooCoAPIPStack.h"
|
||||
#include "TembooTimer.h"
|
||||
#include "CoapMsg.h"
|
||||
|
||||
/**
|
||||
* CoapMessageLayer is the lowest layer of the CoAP stack. It is responsible for
|
||||
* transmitting and receiving messages. Specifically, this implementation is
|
||||
* responsible for handling confirmable (CON) messages and their ACKs.
|
||||
* It is intended to implement the Message Layer FSM described by Kovatsch et al. in
|
||||
* https://tools.ietf.org/html/draft-kovatsch-lwig-coap-01
|
||||
*
|
||||
* It can send reliable (confirmable or CON) messages and will maintain the necessary
|
||||
* state information to wait for an acknowledgement. It will handle any necessary
|
||||
* retransmissions and timeouts until a CON message has been ACK'd (or rejected.)
|
||||
*
|
||||
* It can not send unreliable (non-confirmable or NON) messages as currently designed
|
||||
* because our application does not use NON messages.
|
||||
*
|
||||
* It can receive CON and NON messages and will send ACKs or RESETs as required.
|
||||
*
|
||||
* Any non-rejected CON or NON messages received are passed up to the Request/Response
|
||||
* layer (CoapRRLayer) for processing.
|
||||
*
|
||||
* Be sure to note the difference between ACK's and Responses. This layer handles
|
||||
* ACK's, it does not handle Responses. (Responses are handled by the CoapRRLayer class.)
|
||||
*/
|
||||
|
||||
|
||||
class CoapMessageLayer {
|
||||
public:
|
||||
|
||||
enum State {
|
||||
STATE_CLOSED,
|
||||
STATE_RELIABLE_TX,
|
||||
STATE_ACK_PENDING,
|
||||
STATE_WAITING_FOR_CON
|
||||
};
|
||||
|
||||
enum Result {
|
||||
NO_ERROR = 0,
|
||||
CON_RECEIVED,
|
||||
NON_RECEIVED,
|
||||
ACK_RECEIVED,
|
||||
RESET_RECEIVED,
|
||||
ERROR_IMPROPER_STATE,
|
||||
ERROR_NULL_MESSAGE,
|
||||
ERROR_SENDING_PACKET,
|
||||
ERROR_RECEIVING_PACKET,
|
||||
ERROR_RETRANSMIT_COUNT_EXCEEDED,
|
||||
ERROR_TX_SPAN_TIME_EXCEEDED
|
||||
};
|
||||
|
||||
static const uint32_t ACK_TIMEOUT = 2000;
|
||||
|
||||
// MAX_ACK_TIMEOUT = ACK_TIMEOUT * ACK_RANDOM_FACTOR
|
||||
// RFC7252 suggests ACK_RANDOM_FACTOR = 1.5
|
||||
static const uint32_t MAX_ACK_TIMEOUT = 3000;
|
||||
static const uint8_t MAX_RETRANSMIT = 4;
|
||||
|
||||
// MAX_TRANSMIT_SPAN = ACK_TIMEOUT * ((2^MAX_RETRANSMIT) - 1) * ACK_RANDOM_FACTOR
|
||||
static const uint32_t MAX_TRANSMIT_SPAN = 45000;
|
||||
|
||||
// MAX_TRANSMIT_WAIT = ACK_TIMEOUT * (2^(MAX_RETRANSMIT + 1) - 1) * ACK_RANDOM_FACTOR
|
||||
static const uint32_t MAX_TRANSMIT_WAIT = 93000;
|
||||
|
||||
|
||||
CoapMessageLayer(uint8_t* rxBuffer, uint16_t rxLen, TembooCoAPIPStack& ipStack);
|
||||
Result reliableSend(CoapMsg& msg, IPAddress destAddr, uint16_t destPort);
|
||||
Result cancelReliableSend();
|
||||
Result acceptMsg(CoapMsg& msg);
|
||||
Result acceptMsg(CoapMsg& msg, IPAddress addr, uint16_t port);
|
||||
Result rejectMsg(CoapMsg& msg);
|
||||
Result rejectMsg(CoapMsg& msg, IPAddress addr, uint16_t port);
|
||||
Result loop();
|
||||
Result getLastResult() {return m_lastResult;}
|
||||
uint16_t getRXByteCount() {return m_rxByteCount;}
|
||||
void setState(State state) {m_state = state;}
|
||||
|
||||
|
||||
private:
|
||||
uint8_t* m_rxBuffer;
|
||||
uint16_t m_rxLen;
|
||||
TembooCoAPIPStack& m_ipStack;
|
||||
|
||||
State m_state;
|
||||
State m_prevState;
|
||||
int m_retransmitCount;
|
||||
Result m_lastResult;
|
||||
IPAddress m_destAddr;
|
||||
uint16_t m_destPort;
|
||||
|
||||
uint16_t m_msgID;
|
||||
uint8_t* m_msgBytes;
|
||||
uint16_t m_msgLen;
|
||||
int32_t m_rxByteCount;
|
||||
TembooTimer m_txSpanTimer;
|
||||
TembooTimer m_retransmitTimer;
|
||||
uint32_t m_retransmitTimeoutMillis;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
714
arduino/libraries/Temboo/src/utility/CoapMsg.cpp
Normal file
714
arduino/libraries/Temboo/src/utility/CoapMsg.cpp
Normal file
@@ -0,0 +1,714 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "CoapMsg.h"
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
const uint8_t COAP_VERSION = 1;
|
||||
const uint8_t HEADER_LENGTH = 4;
|
||||
|
||||
|
||||
CoapMsg::CoapMsg(uint8_t* buffer, uint16_t bufferLen) : m_buffer(buffer), m_bufferLen(bufferLen) {
|
||||
|
||||
// Starting a new message. All messages have a header.
|
||||
m_msgLen = HEADER_LENGTH;
|
||||
memset(m_buffer, 0, m_bufferLen);
|
||||
m_buffer[0] = COAP_VERSION << 6;
|
||||
m_buildState = BUILD_BEGIN;
|
||||
m_lastOptionCode = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMsg::CoapMsg(uint8_t* buffer, uint16_t bufferLen, uint16_t packetLen) : m_buffer(buffer), m_bufferLen(bufferLen) {
|
||||
m_msgLen = packetLen;
|
||||
m_buildState = BUILD_HAVE_PAYLOAD;
|
||||
m_lastOptionCode = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CoapMsg::setType(CoapMsg::Type msgType) {
|
||||
m_buffer[0] &= 0xCF; //11001111
|
||||
m_buffer[0] |= msgType << 4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMsg::Type CoapMsg::getType() {
|
||||
return (CoapMsg::Type)((m_buffer[0] & 0x30) >> 4);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CoapMsg::setId(uint16_t msgId) {
|
||||
m_buffer[2] = msgId >> 8;
|
||||
m_buffer[3] = msgId & 0x00FF;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint16_t CoapMsg::getId() {
|
||||
return (m_buffer[2] << 8) + m_buffer[3];
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMsg::Result CoapMsg::setToken(const uint8_t* token, uint8_t tokenLen){
|
||||
|
||||
if (m_buildState >= BUILD_HAVE_TOKEN) {
|
||||
return COAP_RESULT_BUILD_ORDER;
|
||||
}
|
||||
|
||||
if (tokenLen > 8){
|
||||
return COAP_RESULT_TOKEN_LENGTH;
|
||||
}
|
||||
|
||||
if ((tokenLen > 0) && (NULL == token)) {
|
||||
return COAP_RESULT_TOKEN_NULL;
|
||||
}
|
||||
|
||||
if ((m_msgLen + tokenLen) > m_bufferLen) {
|
||||
return COAP_RESULT_BUFFER_OVERRUN;
|
||||
}
|
||||
|
||||
memcpy(m_buffer + m_msgLen, token, tokenLen);
|
||||
m_msgLen += tokenLen;
|
||||
m_buffer[0] &= 0xF0;
|
||||
m_buffer[0] |= tokenLen;
|
||||
m_buildState = BUILD_HAVE_TOKEN;
|
||||
|
||||
return COAP_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t* CoapMsg::getToken() {
|
||||
if (getTokenLen() > 0) {
|
||||
return m_buffer + HEADER_LENGTH;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t CoapMsg::getTokenLen() {
|
||||
return m_buffer[0] & 0x0F;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CoapMsg::setCode(CoapMsg::Code code) {
|
||||
m_buffer[1] = (uint8_t)code;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMsg::Code CoapMsg::getCode() {
|
||||
return (CoapMsg::Code)(m_buffer[1]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint16_t CoapMsg::getHTTPStatus() {
|
||||
uint8_t code = getCode();
|
||||
// 3 MSbs are the most significant digit (0 - 6)
|
||||
// 5 LSbs are the two least significant digits (0 - 31)
|
||||
return (code >> 5) * 100 + (code & 0x1F);
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMsg::Result CoapMsg::setPayload(const uint8_t* payload, uint16_t payloadLen) {
|
||||
|
||||
if (m_buildState >= BUILD_HAVE_PAYLOAD) {
|
||||
return COAP_RESULT_BUILD_ORDER;
|
||||
}
|
||||
|
||||
if (payloadLen > 0 && payload == NULL) {
|
||||
return COAP_RESULT_PAYLOAD_NULL;
|
||||
}
|
||||
|
||||
if ((m_msgLen + payloadLen + 1) > m_bufferLen) {
|
||||
return COAP_RESULT_BUFFER_OVERRUN;
|
||||
}
|
||||
|
||||
// Add the special payload marker flag.
|
||||
m_buffer[m_msgLen++] = 0xFF;
|
||||
memcpy(m_buffer + m_msgLen, payload, payloadLen);
|
||||
m_msgLen += payloadLen;
|
||||
m_buildState = BUILD_HAVE_PAYLOAD;
|
||||
|
||||
return COAP_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t* CoapMsg::getPayload() {
|
||||
if (m_buildState < BUILD_HAVE_PAYLOAD) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t* payload = NULL;
|
||||
uint16_t optionDelta = 0;
|
||||
uint16_t optionLen = 0;
|
||||
uint8_t* i = m_buffer + HEADER_LENGTH + getTokenLen();
|
||||
|
||||
while (i < m_buffer + m_msgLen) {
|
||||
i = decodeOption(i, &optionDelta, &optionLen);
|
||||
if ((optionDelta == 15) || (optionLen == 15)) {
|
||||
//Technically, if either optionDelta or optionLen is 15, then
|
||||
//both MUST be 15, else it's a malformed message. However,
|
||||
//we're going to be a little loose with the spec here.
|
||||
payload = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint16_t CoapMsg::getPayloadLen() {
|
||||
if (m_buildState < BUILD_HAVE_PAYLOAD) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t* payload = getPayload();
|
||||
if (NULL == payload) {
|
||||
return 0;
|
||||
}
|
||||
return m_buffer + m_msgLen - payload;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
0 1 2 3 4 5 6 7
|
||||
+---------------+---------------+
|
||||
| | |
|
||||
| Option Delta | Option Length | 1 byte
|
||||
| | |
|
||||
+---------------+---------------+
|
||||
\ \ 0 bytes if OptionDelta <= 12
|
||||
/ Option Delta / 1 byte if OptionDelte == 13
|
||||
\ (extended) \ 2 bytes if OptionDelta == 14
|
||||
+-------------------------------+
|
||||
\ \ 0 bytes if OptionLength <= 12
|
||||
/ Option Length / 1 byte if OptionLength == 13
|
||||
\ (extended) \ 2 bytes if OptionLength == 14
|
||||
+-------------------------------+
|
||||
\ \
|
||||
/ /
|
||||
\ \
|
||||
/ Option Value / 0 or more bytes (i.e. OptionLength bytes)
|
||||
\ \
|
||||
/ /
|
||||
\ \
|
||||
+-------------------------------+
|
||||
*/
|
||||
|
||||
CoapMsg::Result CoapMsg::addOption(CoapMsg::Option optionCode, const uint8_t* optionValue, uint16_t optionLen) {
|
||||
|
||||
if (m_buildState > BUILD_HAVE_OPTIONS) {
|
||||
return COAP_RESULT_BUILD_ORDER;
|
||||
}
|
||||
|
||||
if (m_lastOptionCode > optionCode) {
|
||||
return COAP_RESULT_BUILD_ORDER;
|
||||
}
|
||||
|
||||
if (optionLen > 0 && NULL == optionValue) {
|
||||
return COAP_RESULT_OPTION_NULL;
|
||||
}
|
||||
|
||||
CoapMsg::Result rc = validateOption(optionCode, optionValue, optionLen);
|
||||
if (COAP_RESULT_SUCCESS != rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
// See if there's enough room in the buffer to add this option.
|
||||
uint16_t byteCount = 1;
|
||||
uint16_t optionDelta = optionCode - m_lastOptionCode;
|
||||
if (optionDelta >= 13) {
|
||||
byteCount++;
|
||||
}
|
||||
if (optionDelta >= 269) {
|
||||
byteCount++;
|
||||
}
|
||||
if (optionLen >= 13) {
|
||||
byteCount++;
|
||||
}
|
||||
if (optionLen >= 269) {
|
||||
byteCount++;
|
||||
}
|
||||
byteCount += optionLen;
|
||||
|
||||
if (m_msgLen + byteCount > m_bufferLen) {
|
||||
return COAP_RESULT_BUFFER_OVERRUN;
|
||||
}
|
||||
|
||||
// If we get this far, there's enough room.
|
||||
byteCount = 0;
|
||||
if (optionDelta >= 269) {
|
||||
m_buffer[m_msgLen] = 14 << 4;
|
||||
byteCount++;
|
||||
m_buffer[m_msgLen + byteCount] = (optionDelta - 269) >> 8;
|
||||
byteCount++;
|
||||
m_buffer[m_msgLen + byteCount] = (optionDelta - 269) & 0x00FF;
|
||||
} else if (optionDelta >= 13) {
|
||||
m_buffer[m_msgLen] = 13 << 4;
|
||||
byteCount++;
|
||||
m_buffer[m_msgLen + byteCount] = (optionDelta - 13);
|
||||
} else {
|
||||
m_buffer[m_msgLen] = optionDelta << 4;
|
||||
}
|
||||
|
||||
if (optionLen >= 269) {
|
||||
m_buffer[m_msgLen] = (m_buffer[m_msgLen] & 0XF0) | 14;
|
||||
byteCount++;
|
||||
m_buffer[m_msgLen + byteCount] = (optionLen - 269) >> 8;
|
||||
byteCount++;
|
||||
m_buffer[m_msgLen + byteCount] = (optionLen - 269) & 0xFF;
|
||||
} else if (optionLen >= 13) {
|
||||
m_buffer[m_msgLen] = (m_buffer[m_msgLen] & 0xF0) | 13;
|
||||
byteCount++;
|
||||
m_buffer[m_msgLen + byteCount] = (optionLen - 13);
|
||||
} else {
|
||||
m_buffer[m_msgLen] = (m_buffer[m_msgLen] & 0xF0) | optionLen;
|
||||
byteCount++;
|
||||
}
|
||||
|
||||
m_msgLen += byteCount;
|
||||
if (optionLen > 0) {
|
||||
memcpy(m_buffer + m_msgLen, optionValue, optionLen);
|
||||
}
|
||||
m_msgLen += optionLen;
|
||||
m_lastOptionCode = optionCode;
|
||||
return COAP_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMsg::Result CoapMsg::validateOption(CoapMsg::Option optionCode, const uint8_t* optionValue, uint16_t optionLen) {
|
||||
|
||||
CoapMsg::Result rc = COAP_RESULT_SUCCESS;
|
||||
|
||||
switch(optionCode) {
|
||||
case COAP_OPTION_IF_MATCH:
|
||||
rc = validateOptionValue(0, 8, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_URI_HOST:
|
||||
rc = validateOptionValue(1, 255, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_ETAG:
|
||||
rc = validateOptionValue(1, 8, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_IF_NONE_MATCH:
|
||||
rc = validateOptionValue(0, 0, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
//TODO: case COAP_OPTION_OBSERVE:
|
||||
// rc = validateOptionValue(0, 0, optionValue, optionLen);
|
||||
// break;
|
||||
|
||||
case COAP_OPTION_URI_PORT:
|
||||
rc = validateOptionValue(0, 2, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_LOCATION_PATH:
|
||||
rc = validateOptionValue(0, 255, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_URI_PATH:
|
||||
rc = validateOptionValue(0, 255, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_CONTENT_FORMAT:
|
||||
rc = validateOptionValue(0, 2, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_MAX_AGE:
|
||||
rc = validateOptionValue(0, 4, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_URI_QUERY:
|
||||
rc = validateOptionValue(0, 255, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_ACCEPT:
|
||||
rc = validateOptionValue(0, 2, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_LOCATION_QUERY:
|
||||
rc = validateOptionValue(0, 255, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_BLOCK2:
|
||||
rc = validateOptionValue(0, 3, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_BLOCK1:
|
||||
rc = validateOptionValue(0, 3, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_SIZE2:
|
||||
rc = validateOptionValue(0, 4, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_PROXY_URI:
|
||||
rc = validateOptionValue(0, 1034, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_PROXY_SCHEME:
|
||||
rc = validateOptionValue(1, 255, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_SIZE1:
|
||||
rc = validateOptionValue(0, 4, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = COAP_RESULT_OPTION_UNKNOWN;
|
||||
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMsg::Result CoapMsg::validateOptionValue(uint16_t minLen, uint16_t maxLen, const uint8_t* optionValue, uint16_t optionLen) {
|
||||
|
||||
if (optionLen < minLen || optionLen > maxLen) {
|
||||
return COAP_RESULT_OPTION_LENGTH;
|
||||
}
|
||||
if (optionLen > 0 && NULL == optionValue) {
|
||||
return COAP_RESULT_OPTION_NULL;
|
||||
}
|
||||
|
||||
//TODO: Maybe. Validate value format (uint vs string vs opaque)
|
||||
|
||||
return COAP_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t* CoapMsg::getMsgBytes() {
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint16_t CoapMsg::getMsgLen() {
|
||||
return m_msgLen;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t* CoapMsg::decodeOption(uint8_t* buffer, uint16_t* optionDelta, uint16_t* optionLen) {
|
||||
|
||||
*optionDelta = *buffer >> 4;
|
||||
*optionLen = *buffer & 0x0F;
|
||||
buffer++;
|
||||
|
||||
if (13 == *optionDelta) {
|
||||
*optionDelta = *buffer++ + 13;
|
||||
} else if (14 == *optionDelta) {
|
||||
*optionDelta = *buffer++ << 8;
|
||||
*optionDelta += *buffer++;
|
||||
*optionDelta += 269;
|
||||
}
|
||||
|
||||
if (13 == *optionLen) {
|
||||
*optionLen = *buffer++ + 13;
|
||||
} else if (14 == *optionLen) {
|
||||
*optionLen = *buffer++ << 8;
|
||||
*optionLen += *buffer++;
|
||||
*optionLen += 269;
|
||||
}
|
||||
|
||||
if (*optionLen != 15) {
|
||||
buffer += *optionLen;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint16_t CoapMsg::getOptionCount(CoapMsg::Option optionCode) {
|
||||
|
||||
if (m_buildState < BUILD_HAVE_OPTIONS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t count = 0;
|
||||
uint16_t lastOption = 0;
|
||||
uint16_t optionDelta = 0;
|
||||
uint16_t optionLen = 0;
|
||||
uint8_t* i = m_buffer + HEADER_LENGTH + getTokenLen();
|
||||
|
||||
while (i < (m_buffer + m_msgLen) && *i != 0xFF) {
|
||||
i = decodeOption(i, &optionDelta, &optionLen);
|
||||
lastOption += optionDelta;
|
||||
if (lastOption == optionCode) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint16_t CoapMsg::getOptionLen(CoapMsg::Option optionCode, uint16_t index) {
|
||||
|
||||
uint16_t count = 0;
|
||||
uint16_t lastOption = 0;
|
||||
uint16_t optionDelta = 0;
|
||||
uint16_t optionLen = 0;
|
||||
uint8_t* i = m_buffer + HEADER_LENGTH + getTokenLen();
|
||||
|
||||
while (i < (m_buffer + m_msgLen) && *i != 0xFF) {
|
||||
i = decodeOption(i, &optionDelta, &optionLen);
|
||||
lastOption += optionDelta;
|
||||
if (lastOption == optionCode) {
|
||||
if (count == index) {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
optionLen = 0;
|
||||
}
|
||||
return optionLen;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t* CoapMsg::getOptionValue(CoapMsg::Option optionCode, uint16_t index) {
|
||||
uint16_t count = 0;
|
||||
uint16_t lastOption = 0;
|
||||
uint16_t optionDelta = 0;
|
||||
uint16_t optionLen = 0;
|
||||
uint8_t* i = m_buffer + HEADER_LENGTH + getTokenLen();
|
||||
|
||||
while (i < (m_buffer + m_msgLen) && *i != 0xFF) {
|
||||
i = decodeOption(i, &optionDelta, &optionLen);
|
||||
lastOption += optionDelta;
|
||||
if (lastOption == optionCode) {
|
||||
if (count == index) {
|
||||
return i - optionLen;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMsg::Result CoapMsg::getOption(CoapMsg::Option optionCode, uint16_t index, uint8_t*& optionValue, uint16_t& optionLen) {
|
||||
|
||||
uint16_t count = 0;
|
||||
uint16_t lastOption = 0;
|
||||
uint16_t optionDelta = 0;
|
||||
uint16_t optLen = 0;
|
||||
uint8_t* i = m_buffer + HEADER_LENGTH + getTokenLen();
|
||||
|
||||
while (i < (m_buffer + m_msgLen) && *i != 0xFF) {
|
||||
i = decodeOption(i, &optionDelta, &optLen);
|
||||
lastOption += optionDelta;
|
||||
if (lastOption == optionCode) {
|
||||
if (count == index) {
|
||||
optionValue = i - optLen;
|
||||
optionLen = optLen;
|
||||
return COAP_RESULT_SUCCESS;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return COAP_RESULT_OPTION_NOT_FOUND;
|
||||
|
||||
}
|
||||
|
||||
uint16_t CoapMsg::getBlock1Size() {
|
||||
return getBlockSize(COAP_OPTION_BLOCK1);
|
||||
}
|
||||
|
||||
uint16_t CoapMsg::getBlock2Size() {
|
||||
return getBlockSize(COAP_OPTION_BLOCK2);
|
||||
}
|
||||
|
||||
uint16_t CoapMsg::getBlockSize(CoapMsg::Option optionCode) {
|
||||
if (m_buildState < BUILD_HAVE_OPTIONS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t optionLen;
|
||||
uint8_t* optionValue;
|
||||
if (getOption(optionCode, 0, optionValue, optionLen) != COAP_RESULT_SUCCESS) {
|
||||
return 0;
|
||||
}
|
||||
uint16_t blockLen = 16 << (optionValue[optionLen - 1] & 0x07);
|
||||
if (blockLen > 1024) {
|
||||
return 0;
|
||||
}
|
||||
return blockLen;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint32_t CoapMsg::getBlock1Num() {
|
||||
return getBlockNum(COAP_OPTION_BLOCK1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint32_t CoapMsg::getBlock2Num() {
|
||||
return getBlockNum(COAP_OPTION_BLOCK2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint32_t CoapMsg::getBlockNum(CoapMsg::Option optionCode) {
|
||||
if (m_buildState < BUILD_HAVE_OPTIONS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t optionLen;
|
||||
uint8_t* optionValue;
|
||||
if (getOption(optionCode, 0, optionValue, optionLen) != COAP_RESULT_SUCCESS) {
|
||||
return 0;
|
||||
}
|
||||
int32_t blockNum = 0;
|
||||
for (;optionLen > 0; optionLen--) {
|
||||
blockNum <<= 8;
|
||||
blockNum += *optionValue;
|
||||
optionValue++;
|
||||
}
|
||||
blockNum >>= 4;
|
||||
|
||||
return blockNum;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CoapMsg::getBlock1More() {
|
||||
return getBlockMore(COAP_OPTION_BLOCK1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CoapMsg::getBlock2More() {
|
||||
return getBlockMore(COAP_OPTION_BLOCK2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CoapMsg::getBlockMore(CoapMsg::Option optionCode) {
|
||||
if (m_buildState < BUILD_HAVE_OPTIONS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t optionLen;
|
||||
uint8_t* optionValue;
|
||||
if (getOption(optionCode, 0, optionValue, optionLen) != COAP_RESULT_SUCCESS) {
|
||||
return 0;
|
||||
}
|
||||
if (optionLen == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (optionValue[optionLen-1] & 0x08) > 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert this (existing received) message into a reset message.
|
||||
*/
|
||||
void CoapMsg::convertToReset() {
|
||||
setType(COAP_RESET);
|
||||
setCode(COAP_EMPTY);
|
||||
|
||||
// Set token length to 0
|
||||
m_buffer[0] &= 0xF0;
|
||||
|
||||
m_msgLen = HEADER_LENGTH;
|
||||
}
|
||||
|
||||
void CoapMsg::convertToEmptyAck() {
|
||||
setType(COAP_ACK);
|
||||
setCode(COAP_EMPTY);
|
||||
m_buffer[0] &= 0xF0;
|
||||
m_msgLen = HEADER_LENGTH;
|
||||
}
|
||||
|
||||
bool CoapMsg::isValid() {
|
||||
// check packet size
|
||||
if (m_msgLen < 4) {
|
||||
TEMBOO_TRACE("Packet must be a minimum of 4 bytes\n");
|
||||
return false;
|
||||
}
|
||||
// check token length
|
||||
if (getTokenLen() < 0 || getTokenLen() > 8) {
|
||||
TEMBOO_TRACE("Invalid token length\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int16_t responseClass = m_buffer[1] >> 5;
|
||||
// check HTTP Code is between 000-599
|
||||
if (responseClass < 0 || responseClass > 5) {
|
||||
TEMBOO_TRACE("Invalid code\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (HEADER_LENGTH + getTokenLen() == m_msgLen) {
|
||||
// nothing else in the packet
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t count =0;
|
||||
uint16_t lastOption = 0;
|
||||
uint16_t optionDelta = 0;
|
||||
uint16_t optionLen = 0;
|
||||
uint8_t* i = m_buffer + HEADER_LENGTH + getTokenLen();
|
||||
// validate options
|
||||
while (i < (m_buffer + m_msgLen) && *i != 0xFF) {
|
||||
i = decodeOption(i, &optionDelta, &optionLen);
|
||||
lastOption += optionDelta;
|
||||
if (validateOption((Option)lastOption, i - optionLen, optionLen)) {
|
||||
TEMBOO_TRACE("Invalid option\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// if payload marker exists, make sure there is payload data
|
||||
if (*i == 0xFF && (i - m_buffer) > m_msgLen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
192
arduino/libraries/Temboo/src/utility/CoapMsg.h
Normal file
192
arduino/libraries/Temboo/src/utility/CoapMsg.h
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef COAPMSG_H_
|
||||
#define COAPMSG_H_
|
||||
|
||||
/*
|
||||
Byte 0 Byte 1 Byte 2 Byte 3
|
||||
MSB LSB MSB LSB MSB LSB MSB LSB
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
|
||||
|Ver| T | TKL | | Code | | MsgID MSB | | MsgID LSB |
|
||||
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
|
||||
| Token (if any, TKL bytes) ...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Options (if any) ...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|1 1 1 1 1 1 1 1| Payload (if any) ...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
#define RESPONSE_CODE(class, detail) ((class << 5) + detail)
|
||||
|
||||
class CoapMsg {
|
||||
|
||||
public:
|
||||
enum Type {
|
||||
COAP_CONFIRMABLE = 0,
|
||||
COAP_NON_CONFIRMABLE = 1,
|
||||
COAP_ACK = 2,
|
||||
COAP_RESET = 3
|
||||
};
|
||||
|
||||
enum Code {
|
||||
//REQUEST CODES
|
||||
COAP_EMPTY = 0,
|
||||
COAP_GET = 1,
|
||||
COAP_POST = 2,
|
||||
COAP_PUT = 3,
|
||||
COAP_DELETE = 4,
|
||||
|
||||
//RESPONSE CODES
|
||||
COAP_CREATED = RESPONSE_CODE(2,1),
|
||||
COAP_DELETED = RESPONSE_CODE(2,2),
|
||||
COAP_VALID = RESPONSE_CODE(2,3),
|
||||
COAP_CHANGED = RESPONSE_CODE(2,4),
|
||||
COAP_CONTENT = RESPONSE_CODE(2,5),
|
||||
COAP_CONTINUE = RESPONSE_CODE(2,31),
|
||||
COAP_BAD_REQUEST = RESPONSE_CODE(4,0),
|
||||
COAP_UNAUTHORIZED = RESPONSE_CODE(4,1),
|
||||
COAP_BAD_OPTION = RESPONSE_CODE(4,2),
|
||||
COAP_FORBIDDEN = RESPONSE_CODE(4,3),
|
||||
COAP_NOT_FOUND = RESPONSE_CODE(4,4),
|
||||
COAP_METHOD_NOT_ALLOWED = RESPONSE_CODE(4,5),
|
||||
COAP_NOT_ACCEPTABLE = RESPONSE_CODE(4,6),
|
||||
COAP_REQUEST_ENTITY_INCOMPLETE = RESPONSE_CODE(4,8),
|
||||
COAP_PRECONDITION_FAILED = RESPONSE_CODE(4,12),
|
||||
COAP_REQUEST_ENTITY_TOO_LARGE = RESPONSE_CODE(4,13),
|
||||
COAP_UNSUPPORTED_CONTENT_FORMAT = RESPONSE_CODE(4,15),
|
||||
COAP_INTERNAL_SERVER_ERROR = RESPONSE_CODE(5,0),
|
||||
COAP_NOT_IMPLEMENTED = RESPONSE_CODE(5,1),
|
||||
COAP_BAD_GATEWAY = RESPONSE_CODE(5,2),
|
||||
COAP_SERVICE_UNAVAILABLE = RESPONSE_CODE(5,3),
|
||||
COAP_GATEWAY_TIMEOUT = RESPONSE_CODE(5,4),
|
||||
COAP_PROXYING_NOT_SUPPORTED = RESPONSE_CODE(5,5)
|
||||
};
|
||||
|
||||
enum Option {
|
||||
COAP_OPTION_IF_MATCH = 1,
|
||||
COAP_OPTION_URI_HOST = 3,
|
||||
COAP_OPTION_ETAG = 4,
|
||||
COAP_OPTION_IF_NONE_MATCH = 5,
|
||||
//TODO: COAP_OPTION_OBSERVE = 6,
|
||||
COAP_OPTION_URI_PORT = 7,
|
||||
COAP_OPTION_LOCATION_PATH = 8,
|
||||
COAP_OPTION_URI_PATH = 11,
|
||||
COAP_OPTION_CONTENT_FORMAT = 12,
|
||||
COAP_OPTION_MAX_AGE = 14,
|
||||
COAP_OPTION_URI_QUERY = 15,
|
||||
COAP_OPTION_ACCEPT = 17,
|
||||
COAP_OPTION_LOCATION_QUERY = 20,
|
||||
COAP_OPTION_BLOCK2 = 23,
|
||||
COAP_OPTION_BLOCK1 = 27,
|
||||
COAP_OPTION_SIZE2 = 28,
|
||||
COAP_OPTION_PROXY_URI = 35,
|
||||
COAP_OPTION_PROXY_SCHEME = 39,
|
||||
COAP_OPTION_SIZE1 = 60
|
||||
};
|
||||
|
||||
enum Result {
|
||||
COAP_RESULT_SUCCESS = 0, // No error.
|
||||
COAP_RESULT_TOKEN_NULL, // Token length > 0 but NULL pointer given for token value.
|
||||
COAP_RESULT_TOKEN_LENGTH, // Illegal token length value (> 8).
|
||||
COAP_RESULT_PAYLOAD_NULL, // Payload length > 0 but NULL pointer given for payload value.
|
||||
COAP_RESULT_OPTION_UNKNOWN, // An unknown option code was specified.
|
||||
COAP_RESULT_OPTION_NULL, // Option length > 0 but NULL pointer given for option value.
|
||||
COAP_RESULT_OPTION_LENGTH, // Illegal length for option specified.
|
||||
COAP_RESULT_OPTION_NOT_FOUND,// The requested option was not found in the message.
|
||||
COAP_RESULT_BUFFER_OVERRUN, // Adding data would overrun the packet buffer
|
||||
COAP_RESULT_BUILD_ORDER, // Message build order incorrect.
|
||||
COAP_RESULT_INVALID_MSG, // Received message is malformed or invalid.
|
||||
COAP_RESULT_FAILURE // Operation failed or unspecified error
|
||||
};
|
||||
|
||||
CoapMsg(uint8_t* buffer, uint16_t bufferLen);
|
||||
CoapMsg(uint8_t* buffer, uint16_t bufferLen, uint16_t packetLen);
|
||||
|
||||
void setType(CoapMsg::Type msgType);
|
||||
CoapMsg::Type getType();
|
||||
|
||||
void setId(uint16_t msgId);
|
||||
uint16_t getId();
|
||||
|
||||
void setCode(CoapMsg::Code code);
|
||||
CoapMsg::Code getCode();
|
||||
uint16_t getHTTPStatus();
|
||||
|
||||
CoapMsg::Result setToken(const uint8_t* token, uint8_t tokenLen);
|
||||
uint8_t* getToken();
|
||||
uint8_t getTokenLen();
|
||||
|
||||
CoapMsg::Result addOption(CoapMsg::Option optionCode, const uint8_t* optionValue, uint16_t optionLen);
|
||||
CoapMsg::Result getOption(CoapMsg::Option optionCode, uint16_t index, uint8_t*& optionValue, uint16_t& optionLen);
|
||||
uint16_t getOptionCount(CoapMsg::Option optionCode);
|
||||
uint16_t getOptionLen(CoapMsg::Option optionCode, uint16_t index);
|
||||
uint8_t* getOptionValue(CoapMsg::Option optionCode, uint16_t index);
|
||||
|
||||
CoapMsg::Result setPayload(const uint8_t* payload, uint16_t payloadLen);
|
||||
uint8_t* getPayload();
|
||||
uint16_t getPayloadLen();
|
||||
|
||||
uint8_t* getMsgBytes();
|
||||
uint16_t getMsgLen();
|
||||
|
||||
bool isValid();
|
||||
|
||||
uint16_t getBlock1Size();
|
||||
uint32_t getBlock1Num();
|
||||
bool getBlock1More();
|
||||
|
||||
uint16_t getBlock2Size();
|
||||
uint32_t getBlock2Num();
|
||||
bool getBlock2More();
|
||||
|
||||
void convertToReset();
|
||||
void convertToEmptyAck();
|
||||
|
||||
protected:
|
||||
uint8_t* m_buffer;
|
||||
uint16_t m_bufferLen;
|
||||
uint16_t m_msgLen;
|
||||
|
||||
enum BuildState {
|
||||
BUILD_BEGIN,
|
||||
BUILD_HAVE_TOKEN,
|
||||
BUILD_HAVE_OPTIONS,
|
||||
BUILD_HAVE_PAYLOAD
|
||||
};
|
||||
CoapMsg::BuildState m_buildState;
|
||||
uint16_t m_lastOptionCode;
|
||||
|
||||
protected:
|
||||
CoapMsg::Result validateOption(CoapMsg::Option optionCode, const uint8_t* optionValue, uint16_t optionLen);
|
||||
CoapMsg::Result validateOptionValue(uint16_t minLen, uint16_t maxLen, const uint8_t* optionValue, uint16_t optionLen);
|
||||
uint8_t* decodeOption(uint8_t* buffer, uint16_t* optionDelta, uint16_t* optionLen);
|
||||
uint16_t getBlockSize(CoapMsg::Option optionCode);
|
||||
uint32_t getBlockNum(CoapMsg::Option optionCode);
|
||||
bool getBlockMore(CoapMsg::Option optionCode);
|
||||
};
|
||||
|
||||
|
||||
#endif //TEMBOOCOAP_H_
|
||||
176
arduino/libraries/Temboo/src/utility/CoapRRLayer.cpp
Normal file
176
arduino/libraries/Temboo/src/utility/CoapRRLayer.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include "CoapRRLayer.h"
|
||||
|
||||
CoapRRLayer::CoapRRLayer(CoapMessageLayer& messageLayer, uint8_t* rxBuffer, uint16_t rxBufferLen) :
|
||||
m_messageLayer(messageLayer),
|
||||
m_state(STATE_IDLE),
|
||||
m_lastResult(NO_ERROR),
|
||||
m_token(NULL),
|
||||
m_rxBuffer(rxBuffer),
|
||||
m_rxBufferLen(rxBufferLen) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapRRLayer::Result CoapRRLayer::reliableSend(CoapMsg& msg, char* token, IPAddress addr, uint16_t port) {
|
||||
if (m_state != STATE_IDLE) {
|
||||
return ERROR_IMPROPER_STATE;
|
||||
}
|
||||
|
||||
if (CoapMessageLayer::NO_ERROR != m_messageLayer.reliableSend(msg, addr, port)) {
|
||||
return ERROR_SENDING_MSG;
|
||||
}
|
||||
|
||||
m_token = token;
|
||||
m_state = STATE_WAITING;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CoapRRLayer::rxTokenMatches(CoapMsg& msg) {
|
||||
if (msg.getTokenLen() != strlen(m_token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (0 != memcmp(msg.getToken(), m_token, strlen(m_token))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapRRLayer::Result CoapRRLayer::loop() {
|
||||
|
||||
m_lastResult = NO_ERROR;
|
||||
|
||||
switch (m_state) {
|
||||
|
||||
case STATE_IDLE:
|
||||
|
||||
// Pump the receiver.
|
||||
// We're not serving anything, so unless there's an outstanding
|
||||
// request (which would mean we would be in STATE_WAITING, not STATE_IDLE),
|
||||
// we're going to reject or ignore any incoming traffic.
|
||||
switch(m_messageLayer.loop()) {
|
||||
case CON_RECEIVED:
|
||||
{
|
||||
// Explicitly reject any CON messages so the sender will
|
||||
// quit bugging us with retransmissions.
|
||||
CoapMsg msg(m_rxBuffer, m_rxBufferLen, m_messageLayer.getRXByteCount());
|
||||
m_messageLayer.rejectMsg(msg);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Just ignore anything else.
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case STATE_WAITING:
|
||||
// We're waiting for a response to an earlier request.
|
||||
switch(m_messageLayer.loop()) {
|
||||
|
||||
case CoapMessageLayer::NO_ERROR:
|
||||
// Nothing happened. Nothing to do.
|
||||
break;
|
||||
|
||||
case CoapMessageLayer::ACK_RECEIVED:
|
||||
{
|
||||
CoapMsg msg(m_rxBuffer, m_rxBufferLen, m_messageLayer.getRXByteCount());
|
||||
|
||||
// If it wasn't an empty ack, it's a response.
|
||||
// And if the token matches, then it's the response we're waiting for.
|
||||
if (rxTokenMatches(msg)) {
|
||||
m_lastResult = RESPONSE_RECEIVED;
|
||||
m_state = STATE_IDLE;
|
||||
} else {
|
||||
// if ACK is not empty and tokens don't match, an error has occurred
|
||||
if (msg.getTokenLen() != 0) {
|
||||
m_lastResult = ERROR_RECEIVING_RESPONSE;
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("Error: ");
|
||||
TEMBOO_TRACELN("Msg token did not match");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CoapMessageLayer::RESET_RECEIVED:
|
||||
{
|
||||
// If it was a reset, the message should be empty
|
||||
// and there will be no token
|
||||
m_lastResult = RST_RECEIVED;
|
||||
m_state = STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
case CoapMessageLayer::CON_RECEIVED:
|
||||
{
|
||||
// See if this is our response or just some random message.
|
||||
// The message layer has already confirmed it's from the right host.
|
||||
CoapMsg msg(m_rxBuffer, m_rxBufferLen, m_messageLayer.getRXByteCount());
|
||||
|
||||
// We only accept responses for the current request (i.e. the tokens must match)
|
||||
if (rxTokenMatches(msg)) {
|
||||
m_lastResult = RESPONSE_RECEIVED;
|
||||
m_state = STATE_IDLE;
|
||||
} else {
|
||||
m_messageLayer.rejectMsg(msg);
|
||||
TEMBOO_TRACE("Error: ");
|
||||
TEMBOO_TRACELN("Msg token did not match");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CoapMessageLayer::NON_RECEIVED:
|
||||
{
|
||||
CoapMsg msg(m_rxBuffer, m_rxBufferLen, m_messageLayer.getRXByteCount());
|
||||
if (rxTokenMatches(msg)) {
|
||||
m_lastResult = RESPONSE_RECEIVED;
|
||||
m_state = STATE_IDLE;
|
||||
} else {
|
||||
// if the token does not match, then an error occurred
|
||||
m_lastResult = ERROR_RECEIVING_RESPONSE;
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("Error: ");
|
||||
TEMBOO_TRACELN("Msg token did not match");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Anything else indicates a failure of some sort. Check
|
||||
// the messageLayer lastResult for specifics.
|
||||
m_lastResult = ERROR_RECEIVING_RESPONSE;
|
||||
m_state = STATE_IDLE;
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return m_lastResult;
|
||||
}
|
||||
82
arduino/libraries/Temboo/src/utility/CoapRRLayer.h
Normal file
82
arduino/libraries/Temboo/src/utility/CoapRRLayer.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef COAPRRLAYER_H_
|
||||
#define COAPRRLAYER_H_
|
||||
|
||||
#include "CoapMessageLayer.h"
|
||||
#include "CoapMsg.h"
|
||||
|
||||
/**
|
||||
* CoapRRLayer (Request/Response layer) is a middle layer of the CoAP stack. It is responsible for
|
||||
* sending requests and receiving responses to those requests. Note that it does not handle
|
||||
* ACKs or RESETs. Those are handled at the CoapMessageLayer.
|
||||
*
|
||||
* This class is intended to implement the CoAP Client Request/Response Layer FSM as described
|
||||
* by Kovatsch et al. in https://tools.ietf.org/html/draft-kovatsch-lwig-coap-01
|
||||
*
|
||||
* Note that this design only implements the client functionality as our application does not
|
||||
* serve anything.
|
||||
*/
|
||||
|
||||
class CoapRRLayer {
|
||||
|
||||
public:
|
||||
enum Result {
|
||||
NO_ERROR = 0,
|
||||
RESPONSE_RECEIVED,
|
||||
ACK_RECEIVED,
|
||||
CON_RECEIVED,
|
||||
ERROR_IMPROPER_STATE,
|
||||
ERROR_SENDING_MSG,
|
||||
ERROR_RECEIVING_RESPONSE,
|
||||
RST_RECEIVED
|
||||
};
|
||||
|
||||
enum State {
|
||||
STATE_IDLE,
|
||||
STATE_WAITING
|
||||
};
|
||||
|
||||
CoapRRLayer(CoapMessageLayer& messageLayer, uint8_t* rxBuffer, uint16_t rxBufferLen);
|
||||
|
||||
Result reliableSend(CoapMsg& msg, char* token, IPAddress addr, uint16_t port);
|
||||
Result send(CoapMsg& msg, char* token, IPAddress addr, uint16_t port);
|
||||
Result loop();
|
||||
Result getLastResult() {return m_lastResult;}
|
||||
void setState(State state) {m_state = state;}
|
||||
int16_t getRxByteCount() {return m_rxByteCount;}
|
||||
|
||||
protected:
|
||||
|
||||
CoapMessageLayer& m_messageLayer;
|
||||
State m_state;
|
||||
Result m_lastResult;
|
||||
char* m_token;
|
||||
uint8_t* m_rxBuffer;
|
||||
int16_t m_rxByteCount;
|
||||
uint16_t m_rxBufferLen;
|
||||
|
||||
bool rxTokenMatches(CoapMsg& msg);
|
||||
};
|
||||
|
||||
#endif
|
||||
226
arduino/libraries/Temboo/src/utility/DataFormatter.cpp
Normal file
226
arduino/libraries/Temboo/src/utility/DataFormatter.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include "DataFormatter.h"
|
||||
|
||||
|
||||
DataFormatter::DataFormatter(const ChoreoInputSet* inputSet, const ChoreoInputExpressionSet* expressionSet, const ChoreoSensorInputSet* sensorSet, const ChoreoOutputSet* outputSet, const ChoreoPreset* preset, const ChoreoDevice* device, const ChoreoDevice* deviceName) :
|
||||
m_inputFormatter(inputSet),
|
||||
m_expressionFormatter(expressionSet),
|
||||
m_sensorFormatter(sensorSet),
|
||||
m_outputFormatter(outputSet),
|
||||
m_presetFormatter(preset),
|
||||
m_deviceTypeFormatter(device, ChoreoDeviceFormatter::DEVICE_TYPE),
|
||||
m_deviceNameFormatter(deviceName, ChoreoDeviceFormatter::DEVICE_NAME) {
|
||||
|
||||
m_inputSet = inputSet;
|
||||
m_expressionSet = expressionSet;
|
||||
m_sensorSet = sensorSet;
|
||||
m_outputSet = outputSet;
|
||||
m_preset = preset;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void DataFormatter::reset() {
|
||||
m_nextState = DATA_START;
|
||||
m_inputFormatter.reset();
|
||||
m_expressionFormatter.reset();
|
||||
m_sensorFormatter.reset();
|
||||
m_outputFormatter.reset();
|
||||
m_presetFormatter.reset();
|
||||
m_deviceTypeFormatter.reset();
|
||||
m_deviceNameFormatter.reset();
|
||||
}
|
||||
|
||||
bool DataFormatter::hasNext() {
|
||||
return m_nextState != DATA_END;
|
||||
}
|
||||
|
||||
char DataFormatter::next() {
|
||||
char c;
|
||||
switch(m_nextState) {
|
||||
case DATA_START:
|
||||
c = '{';
|
||||
// only add device type or device name if
|
||||
// sensor data is being sent with the Choreo
|
||||
// If device name is added, no need to send
|
||||
// device type
|
||||
if (m_sensorFormatter.hasNext()) {
|
||||
if (m_deviceNameFormatter.hasNext()) {
|
||||
m_nextState = FORMATTING_DEVICE_NAME;
|
||||
} else if (m_deviceTypeFormatter.hasNext()) {
|
||||
m_nextState = FORMATTING_DEVICE_TYPE;
|
||||
}
|
||||
} else {
|
||||
if (m_inputFormatter.hasNext()) {
|
||||
m_nextState = FORMATTING_INPUTS;
|
||||
} else if (m_expressionFormatter.hasNext()) {
|
||||
m_nextState = FORMATTING_EXPRESSIONS;
|
||||
} else if (m_sensorFormatter.hasNext()) {
|
||||
m_nextState = FORMATTING_SENSORS;
|
||||
} else if (m_outputFormatter.hasNext()) {
|
||||
m_nextState = FORMATTING_OUTPUTS;
|
||||
} else if (m_presetFormatter.hasNext()) {
|
||||
m_nextState = FORMATTING_PRESET;
|
||||
} else {
|
||||
m_nextState = FORMATTING_EMPTY;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FORMATTING_DEVICE_TYPE:
|
||||
if (m_deviceTypeFormatter.hasNext()) {
|
||||
c = m_deviceTypeFormatter.next();
|
||||
} else if (m_inputFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_INPUTS;
|
||||
} else if (m_expressionFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_EXPRESSIONS;
|
||||
} else if (m_sensorFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_SENSORS;
|
||||
} else if (m_outputFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_OUTPUTS;
|
||||
} else if (m_presetFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_PRESET;
|
||||
} else {
|
||||
c = '}';
|
||||
m_nextState = DATA_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case FORMATTING_DEVICE_NAME:
|
||||
if (m_deviceNameFormatter.hasNext()) {
|
||||
c = m_deviceNameFormatter.next();
|
||||
} else if (m_inputFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_INPUTS;
|
||||
} else if (m_expressionFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_EXPRESSIONS;
|
||||
} else if (m_sensorFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_SENSORS;
|
||||
} else if (m_outputFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_OUTPUTS;
|
||||
} else if (m_presetFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_PRESET;
|
||||
} else {
|
||||
c = '}';
|
||||
m_nextState = DATA_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case FORMATTING_INPUTS:
|
||||
if (m_inputFormatter.hasNext()) {
|
||||
c = m_inputFormatter.next();
|
||||
} else if (m_expressionFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_EXPRESSIONS;
|
||||
} else if (m_sensorFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_SENSORS;
|
||||
} else if (m_outputFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_OUTPUTS;
|
||||
} else if (m_presetFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_PRESET;
|
||||
} else {
|
||||
c = '}';
|
||||
m_nextState = DATA_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case FORMATTING_EXPRESSIONS:
|
||||
if (m_expressionFormatter.hasNext()) {
|
||||
c = m_expressionFormatter.next();
|
||||
} else if (m_sensorFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_SENSORS;
|
||||
} else if (m_outputFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_OUTPUTS;
|
||||
} else if (m_presetFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_PRESET;
|
||||
} else {
|
||||
c = '}';
|
||||
m_nextState = DATA_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case FORMATTING_SENSORS:
|
||||
if (m_sensorFormatter.hasNext()) {
|
||||
c = m_sensorFormatter.next();
|
||||
} else if (m_outputFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_OUTPUTS;
|
||||
} else if (m_presetFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_PRESET;
|
||||
} else {
|
||||
c = '}';
|
||||
m_nextState = DATA_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case FORMATTING_OUTPUTS:
|
||||
if (m_outputFormatter.hasNext()) {
|
||||
c = m_outputFormatter.next();
|
||||
} else if (m_presetFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_PRESET;
|
||||
} else {
|
||||
c = '}';
|
||||
m_nextState = DATA_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case FORMATTING_PRESET:
|
||||
if (m_presetFormatter.hasNext()) {
|
||||
c = m_presetFormatter.next();
|
||||
} else {
|
||||
c = '}';
|
||||
m_nextState = DATA_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case FORMATTING_EMPTY:
|
||||
c = '}';
|
||||
m_nextState = DATA_END;
|
||||
break;
|
||||
|
||||
case DATA_END:
|
||||
default:
|
||||
c = '\0';
|
||||
break;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
80
arduino/libraries/Temboo/src/utility/DataFormatter.h
Normal file
80
arduino/libraries/Temboo/src/utility/DataFormatter.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef DATAFORMATTER_H_
|
||||
#define DATAFORMATTER_H_
|
||||
#include "TembooGlobal.h"
|
||||
#include "ChoreoInputSet.h"
|
||||
#include "ChoreoInputExpressionSet.h"
|
||||
#include "ChoreoSensorInputSet.h"
|
||||
#include "ChoreoOutputSet.h"
|
||||
#include "ChoreoPreset.h"
|
||||
#include "ChoreoDevice.h"
|
||||
#include "ChoreoInputFormatter.h"
|
||||
#include "ChoreoInputExpressionFormatter.h"
|
||||
#include "ChoreoSensorInputFormatter.h"
|
||||
#include "ChoreoOutputFormatter.h"
|
||||
#include "ChoreoPresetFormatter.h"
|
||||
#include "ChoreoDeviceFormatter.h"
|
||||
|
||||
|
||||
|
||||
class DataFormatter {
|
||||
|
||||
public:
|
||||
DataFormatter(const ChoreoInputSet* inputSet, const ChoreoInputExpressionSet* expressionSet, const ChoreoSensorInputSet* sensorSet, const ChoreoOutputSet* outputSet, const ChoreoPreset* preset, const ChoreoDevice* device, const ChoreoDevice* deviceName);
|
||||
bool hasNext();
|
||||
char next();
|
||||
void reset();
|
||||
|
||||
private:
|
||||
const ChoreoInputSet* m_inputSet;
|
||||
const ChoreoSensorInputSet* m_sensorSet;
|
||||
const ChoreoInputExpressionSet* m_expressionSet;
|
||||
const ChoreoOutputSet* m_outputSet;
|
||||
const ChoreoPreset* m_preset;
|
||||
|
||||
ChoreoInputFormatter m_inputFormatter;
|
||||
ChoreoInputExpressionFormatter m_expressionFormatter;
|
||||
ChoreoSensorInputFormatter m_sensorFormatter;
|
||||
ChoreoOutputFormatter m_outputFormatter;
|
||||
ChoreoPresetFormatter m_presetFormatter;
|
||||
ChoreoDeviceFormatter m_deviceTypeFormatter;
|
||||
ChoreoDeviceFormatter m_deviceNameFormatter;
|
||||
|
||||
enum State {
|
||||
DATA_START,
|
||||
FORMATTING_INPUTS,
|
||||
FORMATTING_EXPRESSIONS,
|
||||
FORMATTING_SENSORS,
|
||||
FORMATTING_OUTPUTS,
|
||||
FORMATTING_PRESET,
|
||||
FORMATTING_DEVICE_TYPE,
|
||||
FORMATTING_DEVICE_NAME,
|
||||
FORMATTING_EMPTY,
|
||||
DATA_END
|
||||
};
|
||||
|
||||
State m_nextState;
|
||||
|
||||
};
|
||||
#endif
|
||||
208
arduino/libraries/Temboo/src/utility/FP.h
Normal file
208
arduino/libraries/Temboo/src/utility/FP.h
Normal file
@@ -0,0 +1,208 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2013, 2014
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Sam Grove - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - added attached and detached member functions
|
||||
* Sam Grove - removed need for FP.cpp
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef FP_H
|
||||
#define FP_H
|
||||
|
||||
/** Example using the FP Class with global functions
|
||||
* @code
|
||||
* #include "mbed.h"
|
||||
* #include "FP.h"
|
||||
*
|
||||
* FP<void,bool>fp;
|
||||
* DigitalOut myled(LED1);
|
||||
*
|
||||
* void handler(bool value)
|
||||
* {
|
||||
* myled = value;
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* int main()
|
||||
* {
|
||||
* fp.attach(&handler);
|
||||
*
|
||||
* while(1)
|
||||
* {
|
||||
* fp(1);
|
||||
* wait(0.2);
|
||||
* fp(0);
|
||||
* wait(0.2);
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
/** Example using the FP Class with different class member functions
|
||||
* @code
|
||||
* #include "mbed.h"
|
||||
* #include "FP.h"
|
||||
*
|
||||
* FP<void,bool>fp;
|
||||
* DigitalOut myled(LED4);
|
||||
*
|
||||
* class Wrapper
|
||||
* {
|
||||
* public:
|
||||
* Wrapper(){}
|
||||
*
|
||||
* void handler(bool value)
|
||||
* {
|
||||
* myled = value;
|
||||
* return;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* int main()
|
||||
* {
|
||||
* Wrapper wrapped;
|
||||
* fp.attach(&wrapped, &Wrapper::handler);
|
||||
*
|
||||
* while(1)
|
||||
* {
|
||||
* fp(1);
|
||||
* wait(0.2);
|
||||
* fp(0);
|
||||
* wait(0.2);
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
/** Example using the FP Class with member FP and member function
|
||||
* @code
|
||||
* #include "mbed.h"
|
||||
* #include "FP.h"
|
||||
*
|
||||
* DigitalOut myled(LED2);
|
||||
*
|
||||
* class Wrapper
|
||||
* {
|
||||
* public:
|
||||
* Wrapper()
|
||||
* {
|
||||
* fp.attach(this, &Wrapper::handler);
|
||||
* }
|
||||
*
|
||||
* void handler(bool value)
|
||||
* {
|
||||
* myled = value;
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* FP<void,bool>fp;
|
||||
* };
|
||||
*
|
||||
* int main()
|
||||
* {
|
||||
* Wrapper wrapped;
|
||||
*
|
||||
* while(1)
|
||||
* {
|
||||
* wrapped.fp(1);
|
||||
* wait(0.2);
|
||||
* wrapped.fp(0);
|
||||
* wait(0.2);
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class FP
|
||||
* @brief API for managing Function Pointers
|
||||
*/
|
||||
template<class retT, class argT>
|
||||
class FP
|
||||
{
|
||||
public:
|
||||
/** Create the FP object - only one callback can be attached to the object, that is
|
||||
* a member function or a global function, not both at the same time
|
||||
*/
|
||||
FP()
|
||||
{
|
||||
obj_callback = 0;
|
||||
c_callback = 0;
|
||||
}
|
||||
|
||||
/** Add a callback function to the object
|
||||
* @param item - Address of the initialized object
|
||||
* @param member - Address of the member function (dont forget the scope that the function is defined in)
|
||||
*/
|
||||
template<class T>
|
||||
void attach(T *item, retT (T::*method)(argT))
|
||||
{
|
||||
obj_callback = (FPtrDummy *)(item);
|
||||
method_callback = (retT (FPtrDummy::*)(argT))(method);
|
||||
return;
|
||||
}
|
||||
|
||||
/** Add a callback function to the object
|
||||
* @param function - The address of a globally defined function
|
||||
*/
|
||||
void attach(retT (*function)(argT))
|
||||
{
|
||||
c_callback = function;
|
||||
}
|
||||
|
||||
/** Invoke the function attached to the class
|
||||
* @param arg - An argument that is passed into the function handler that is called
|
||||
* @return The return from the function hanlder called by this class
|
||||
*/
|
||||
retT operator()(argT arg) const
|
||||
{
|
||||
if( 0 != c_callback ) {
|
||||
return obj_callback ? (obj_callback->*method_callback)(arg) : (*c_callback)(arg);
|
||||
}
|
||||
return (retT)0;
|
||||
}
|
||||
|
||||
/** Determine if an callback is currently hooked
|
||||
* @return 1 if a method is hooked, 0 otherwise
|
||||
*/
|
||||
bool attached()
|
||||
{
|
||||
return obj_callback || c_callback;
|
||||
}
|
||||
|
||||
/** Release a function from the callback hook
|
||||
*/
|
||||
void detach()
|
||||
{
|
||||
obj_callback = 0;
|
||||
c_callback = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// empty type used for casting
|
||||
class FPtrDummy;
|
||||
|
||||
FPtrDummy *obj_callback;
|
||||
|
||||
/**
|
||||
* @union Funciton
|
||||
* @brief Member or global callback function
|
||||
*/
|
||||
union {
|
||||
retT (*c_callback)(argT); /*!< Footprint for a global function */
|
||||
retT (FPtrDummy::*method_callback)(argT); /*!< Footprint for a member function */
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
954
arduino/libraries/Temboo/src/utility/MQTTClient.h
Normal file
954
arduino/libraries/Temboo/src/utility/MQTTClient.h
Normal file
@@ -0,0 +1,954 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014, 2015 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - fix for bug 458512 - QoS 2 messages
|
||||
* Ian Craggs - fix for bug 460389 - send loop uses wrong length
|
||||
* Ian Craggs - fix for bug 464169 - clearing subscriptions
|
||||
* Ian Craggs - fix for bug 464551 - enums and ints can be different size
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTCLIENT_H)
|
||||
#define MQTTCLIENT_H
|
||||
|
||||
#include "FP.h"
|
||||
#include "MQTTPacket.h"
|
||||
#include "stdio.h"
|
||||
#include "MQTTLogging.h"
|
||||
|
||||
#if !defined(MQTTCLIENT_QOS1)
|
||||
#define MQTTCLIENT_QOS1 1
|
||||
#endif
|
||||
#if !defined(MQTTCLIENT_QOS2)
|
||||
#define MQTTCLIENT_QOS2 0
|
||||
#endif
|
||||
|
||||
namespace MQTT
|
||||
{
|
||||
|
||||
|
||||
enum QoS { QOS0, QOS1, QOS2 };
|
||||
|
||||
// all failure return codes must be negative
|
||||
enum returnCode { BUFFER_OVERFLOW = -2, FAILURE = -1, SUCCESS = 0 };
|
||||
|
||||
|
||||
struct Message
|
||||
{
|
||||
enum QoS qos;
|
||||
bool retained;
|
||||
bool dup;
|
||||
unsigned short id;
|
||||
void *payload;
|
||||
size_t payloadlen;
|
||||
};
|
||||
|
||||
|
||||
struct MessageData
|
||||
{
|
||||
MessageData(MQTTString &aTopicName, struct Message &aMessage) : message(aMessage), topicName(aTopicName)
|
||||
{ }
|
||||
|
||||
struct Message &message;
|
||||
MQTTString &topicName;
|
||||
};
|
||||
|
||||
|
||||
class PacketId
|
||||
{
|
||||
public:
|
||||
PacketId()
|
||||
{
|
||||
next = 0;
|
||||
}
|
||||
|
||||
int getNext()
|
||||
{
|
||||
next = (next == MAX_PACKET_ID) ? 1 : next + 1;
|
||||
return next;
|
||||
}
|
||||
|
||||
private:
|
||||
static const int MAX_PACKET_ID = 65535;
|
||||
int next;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @class Client
|
||||
* @brief blocking, non-threaded MQTT client API
|
||||
*
|
||||
* This version of the API blocks on all method calls, until they are complete. This means that only one
|
||||
* MQTT request can be in process at any one time.
|
||||
* @param Network a network class which supports send, receive
|
||||
* @param Timer a timer class with the methods:
|
||||
*/
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE = 100, int MAX_MESSAGE_HANDLERS = 5>
|
||||
class Client
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
typedef void (*messageHandler)(MessageData&);
|
||||
|
||||
/** Construct the client
|
||||
* @param network - pointer to an instance of the Network class - must be connected to the endpoint
|
||||
* before calling MQTT connect
|
||||
* @param limits an instance of the Limit class - to alter limits as required
|
||||
*/
|
||||
Client(Network& network, unsigned int command_timeout_ms = 30000);
|
||||
|
||||
/** Set the default message handling callback - used for any message which does not match a subscription message handler
|
||||
* @param mh - pointer to the callback function
|
||||
*/
|
||||
void setDefaultMessageHandler(messageHandler mh)
|
||||
{
|
||||
defaultMessageHandler.attach(mh);
|
||||
}
|
||||
|
||||
/** MQTT Connect - send an MQTT connect packet down the network and wait for a Connack
|
||||
* The nework object must be connected to the network endpoint before calling this
|
||||
* Default connect options are used
|
||||
* @return success code -
|
||||
*/
|
||||
int connect();
|
||||
|
||||
/** MQTT Connect - send an MQTT connect packet down the network and wait for a Connack
|
||||
* The nework object must be connected to the network endpoint before calling this
|
||||
* @param options - connect options
|
||||
* @return success code -
|
||||
*/
|
||||
int connect(MQTTPacket_connectData& options);
|
||||
|
||||
/** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs
|
||||
* @param topic - the topic to publish to
|
||||
* @param message - the message to send
|
||||
* @return success code -
|
||||
*/
|
||||
int publish(const char* topicName, Message& message);
|
||||
|
||||
/** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs
|
||||
* @param topic - the topic to publish to
|
||||
* @param payload - the data to send
|
||||
* @param payloadlen - the length of the data
|
||||
* @param qos - the QoS to send the publish at
|
||||
* @param retained - whether the message should be retained
|
||||
* @return success code -
|
||||
*/
|
||||
int publish(const char* topicName, void* payload, size_t payloadlen, enum QoS qos = QOS0, bool retained = false);
|
||||
|
||||
/** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs
|
||||
* @param topic - the topic to publish to
|
||||
* @param payload - the data to send
|
||||
* @param payloadlen - the length of the data
|
||||
* @param id - the packet id used - returned
|
||||
* @param qos - the QoS to send the publish at
|
||||
* @param retained - whether the message should be retained
|
||||
* @return success code -
|
||||
*/
|
||||
int publish(const char* topicName, void* payload, size_t payloadlen, unsigned short& id, enum QoS qos = QOS1, bool retained = false);
|
||||
|
||||
/** MQTT Subscribe - send an MQTT subscribe packet and wait for the suback
|
||||
* @param topicFilter - a topic pattern which can include wildcards
|
||||
* @param qos - the MQTT QoS to subscribe at
|
||||
* @param mh - the callback function to be invoked when a message is received for this subscription
|
||||
* @return success code -
|
||||
*/
|
||||
int subscribe(const char* topicFilter, enum QoS qos, messageHandler mh);
|
||||
|
||||
/** MQTT Unsubscribe - send an MQTT unsubscribe packet and wait for the unsuback
|
||||
* @param topicFilter - a topic pattern which can include wildcards
|
||||
* @return success code -
|
||||
*/
|
||||
int unsubscribe(const char* topicFilter);
|
||||
|
||||
/** MQTT Disconnect - send an MQTT disconnect packet, and clean up any state
|
||||
* @return success code -
|
||||
*/
|
||||
int disconnect();
|
||||
|
||||
/** A call to this API must be made within the keepAlive interval to keep the MQTT connection alive
|
||||
* yield can be called if no other MQTT operation is needed. This will also allow messages to be
|
||||
* received.
|
||||
* @param timeout_ms the time to wait, in milliseconds
|
||||
* @return success code - on failure, this means the client has disconnected
|
||||
*/
|
||||
int yield(unsigned long timeout_ms = 1000L);
|
||||
|
||||
/** Is the client connected?
|
||||
* @return flag - is the client connected or not?
|
||||
*/
|
||||
bool isConnected()
|
||||
{
|
||||
return isconnected;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void cleanSession();
|
||||
int cycle(Timer& timer);
|
||||
int waitfor(int packet_type, Timer& timer);
|
||||
int keepalive();
|
||||
int publish(int len, Timer& timer, enum QoS qos);
|
||||
|
||||
int decodePacket(int* value, int timeout);
|
||||
int readPacket(Timer& timer);
|
||||
int sendPacket(int length, Timer& timer);
|
||||
int deliverMessage(MQTTString& topicName, Message& message);
|
||||
bool isTopicMatched(char* topicFilter, MQTTString& topicName);
|
||||
|
||||
Network& ipstack;
|
||||
unsigned long command_timeout_ms;
|
||||
|
||||
unsigned char sendbuf[MAX_MQTT_PACKET_SIZE];
|
||||
unsigned char readbuf[MAX_MQTT_PACKET_SIZE];
|
||||
|
||||
Timer last_sent, last_received;
|
||||
unsigned int keepAliveInterval;
|
||||
bool ping_outstanding;
|
||||
bool cleansession;
|
||||
|
||||
PacketId packetid;
|
||||
|
||||
struct MessageHandlers
|
||||
{
|
||||
const char* topicFilter;
|
||||
FP<void, MessageData&> fp;
|
||||
} messageHandlers[MAX_MESSAGE_HANDLERS]; // Message handlers are indexed by subscription topic
|
||||
|
||||
FP<void, MessageData&> defaultMessageHandler;
|
||||
|
||||
bool isconnected;
|
||||
|
||||
#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
|
||||
unsigned char pubbuf[MAX_MQTT_PACKET_SIZE]; // store the last publish for sending on reconnect
|
||||
int inflightLen;
|
||||
unsigned short inflightMsgid;
|
||||
enum QoS inflightQoS;
|
||||
#endif
|
||||
|
||||
#if MQTTCLIENT_QOS2
|
||||
bool pubrel;
|
||||
#if !defined(MAX_INCOMING_QOS2_MESSAGES)
|
||||
#define MAX_INCOMING_QOS2_MESSAGES 10
|
||||
#endif
|
||||
unsigned short incomingQoS2messages[MAX_INCOMING_QOS2_MESSAGES];
|
||||
bool isQoS2msgidFree(unsigned short id);
|
||||
bool useQoS2msgid(unsigned short id);
|
||||
void freeQoS2msgid(unsigned short id);
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int a, int MAX_MESSAGE_HANDLERS>
|
||||
void MQTT::Client<Network, Timer, a, MAX_MESSAGE_HANDLERS>::cleanSession()
|
||||
{
|
||||
ping_outstanding = false;
|
||||
for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
|
||||
messageHandlers[i].topicFilter = 0;
|
||||
isconnected = false;
|
||||
|
||||
#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
|
||||
inflightMsgid = 0;
|
||||
inflightQoS = QOS0;
|
||||
#endif
|
||||
|
||||
#if MQTTCLIENT_QOS2
|
||||
pubrel = false;
|
||||
for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i)
|
||||
incomingQoS2messages[i] = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int a, int MAX_MESSAGE_HANDLERS>
|
||||
MQTT::Client<Network, Timer, a, MAX_MESSAGE_HANDLERS>::Client(Network& network, unsigned int command_timeout_ms) : ipstack(network), packetid()
|
||||
{
|
||||
last_sent = Timer();
|
||||
last_received = Timer();
|
||||
this->command_timeout_ms = command_timeout_ms;
|
||||
cleanSession();
|
||||
}
|
||||
|
||||
|
||||
#if MQTTCLIENT_QOS2
|
||||
template<class Network, class Timer, int a, int b>
|
||||
bool MQTT::Client<Network, Timer, a, b>::isQoS2msgidFree(unsigned short id)
|
||||
{
|
||||
for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i)
|
||||
{
|
||||
if (incomingQoS2messages[i] == id)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int a, int b>
|
||||
bool MQTT::Client<Network, Timer, a, b>::useQoS2msgid(unsigned short id)
|
||||
{
|
||||
for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i)
|
||||
{
|
||||
if (incomingQoS2messages[i] == 0)
|
||||
{
|
||||
incomingQoS2messages[i] = id;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int a, int b>
|
||||
void MQTT::Client<Network, Timer, a, b>::freeQoS2msgid(unsigned short id)
|
||||
{
|
||||
for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i)
|
||||
{
|
||||
if (incomingQoS2messages[i] == id)
|
||||
{
|
||||
incomingQoS2messages[i] = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
template<class Network, class Timer, int a, int b>
|
||||
int MQTT::Client<Network, Timer, a, b>::sendPacket(int length, Timer& timer)
|
||||
{
|
||||
int rc = FAILURE,
|
||||
sent = 0;
|
||||
|
||||
while (sent < length && !timer.expired())
|
||||
{
|
||||
rc = ipstack.write(&sendbuf[sent], length - sent, timer.left_ms());
|
||||
if (rc < 0) // there was an error writing the data
|
||||
break;
|
||||
sent += rc;
|
||||
}
|
||||
if (sent == length)
|
||||
{
|
||||
if (this->keepAliveInterval > 0)
|
||||
last_sent.countdown(this->keepAliveInterval); // record the fact that we have successfully sent the packet
|
||||
rc = SUCCESS;
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
|
||||
#if defined(MQTT_DEBUG)
|
||||
char printbuf[150];
|
||||
DEBUG("Rc %d from sending packet %s\n", rc, MQTTFormat_toServerString(printbuf, sizeof(printbuf), sendbuf, length));
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int a, int b>
|
||||
int MQTT::Client<Network, Timer, a, b>::decodePacket(int* value, int timeout)
|
||||
{
|
||||
unsigned char c;
|
||||
int multiplier = 1;
|
||||
int len = 0;
|
||||
const int MAX_NO_OF_REMAINING_LENGTH_BYTES = 4;
|
||||
|
||||
*value = 0;
|
||||
do
|
||||
{
|
||||
int rc = MQTTPACKET_READ_ERROR;
|
||||
|
||||
if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
|
||||
{
|
||||
rc = MQTTPACKET_READ_ERROR; /* bad data */
|
||||
goto exit;
|
||||
}
|
||||
rc = ipstack.read(&c, 1, timeout);
|
||||
if (rc != 1)
|
||||
goto exit;
|
||||
*value += (c & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
} while ((c & 128) != 0);
|
||||
exit:
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If any read fails in this method, then we should disconnect from the network, as on reconnect
|
||||
* the packets can be retried.
|
||||
* @param timeout the max time to wait for the packet read to complete, in milliseconds
|
||||
* @return the MQTT packet type, or -1 if none
|
||||
*/
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::readPacket(Timer& timer)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
MQTTHeader header = {0};
|
||||
int len = 0;
|
||||
int rem_len = 0;
|
||||
|
||||
/* 1. read the header byte. This has the packet type in it */
|
||||
if (ipstack.read(readbuf, 1, timer.left_ms()) != 1)
|
||||
goto exit;
|
||||
|
||||
len = 1;
|
||||
/* 2. read the remaining length. This is variable in itself */
|
||||
decodePacket(&rem_len, timer.left_ms());
|
||||
len += MQTTPacket_encode(readbuf + 1, rem_len); /* put the original remaining length into the buffer */
|
||||
|
||||
if (rem_len > (MAX_MQTT_PACKET_SIZE - len))
|
||||
{
|
||||
rc = BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
/* 3. read the rest of the buffer using a callback to supply the rest of the data */
|
||||
if (rc == BUFFER_OVERFLOW) {
|
||||
if (rem_len > 0 && (ipstack.read(readbuf + len, MAX_MQTT_PACKET_SIZE - len, timer.left_ms()) != (MAX_MQTT_PACKET_SIZE - len)))
|
||||
goto exit;
|
||||
readbuf[MAX_MQTT_PACKET_SIZE-1] = '\0';
|
||||
rem_len = rem_len - MAX_MQTT_PACKET_SIZE - len;
|
||||
while(rem_len--) {
|
||||
unsigned char c;
|
||||
ipstack.read(&c, 1, timer.left_ms() );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (rem_len > 0 && (ipstack.read(readbuf + len, rem_len, timer.left_ms()) != rem_len))
|
||||
goto exit;
|
||||
header.byte = readbuf[0];
|
||||
rc = header.bits.type;
|
||||
}
|
||||
|
||||
if (this->keepAliveInterval > 0)
|
||||
last_received.countdown(this->keepAliveInterval); // record the fact that we have successfully received a packet
|
||||
exit:
|
||||
#if defined(MQTT_DEBUG)
|
||||
if (rc >= 0)
|
||||
{
|
||||
char printbuf[50];
|
||||
DEBUG("Rc %d from receiving packet %s\n", rc, MQTTFormat_toClientString(printbuf, sizeof(printbuf), readbuf, len));
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
// assume topic filter and name is in correct format
|
||||
// # can only be at end
|
||||
// + and # can only be next to separator
|
||||
template<class Network, class Timer, int a, int b>
|
||||
bool MQTT::Client<Network, Timer, a, b>::isTopicMatched(char* topicFilter, MQTTString& topicName)
|
||||
{
|
||||
char* curf = topicFilter;
|
||||
char* curn = topicName.lenstring.data;
|
||||
char* curn_end = curn + topicName.lenstring.len;
|
||||
|
||||
while (*curf && curn < curn_end)
|
||||
{
|
||||
if (*curn == '/' && *curf != '/')
|
||||
break;
|
||||
if (*curf != '+' && *curf != '#' && *curf != *curn)
|
||||
break;
|
||||
if (*curf == '+')
|
||||
{ // skip until we meet the next separator, or end of string
|
||||
char* nextpos = curn + 1;
|
||||
while (nextpos < curn_end && *nextpos != '/')
|
||||
nextpos = ++curn + 1;
|
||||
}
|
||||
else if (*curf == '#')
|
||||
curn = curn_end - 1; // skip until end of string
|
||||
curf++;
|
||||
curn++;
|
||||
};
|
||||
|
||||
return (curn == curn_end) && (*curf == '\0');
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Network, class Timer, int a, int MAX_MESSAGE_HANDLERS>
|
||||
int MQTT::Client<Network, Timer, a, MAX_MESSAGE_HANDLERS>::deliverMessage(MQTTString& topicName, Message& message)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
|
||||
// we have to find the right message handler - indexed by topic
|
||||
for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
|
||||
{
|
||||
if (messageHandlers[i].topicFilter != 0 && (MQTTPacket_equals(&topicName, (char*)messageHandlers[i].topicFilter) ||
|
||||
isTopicMatched((char*)messageHandlers[i].topicFilter, topicName)))
|
||||
{
|
||||
if (messageHandlers[i].fp.attached())
|
||||
{
|
||||
MessageData md(topicName, message);
|
||||
messageHandlers[i].fp(md);
|
||||
rc = SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == FAILURE && defaultMessageHandler.attached())
|
||||
{
|
||||
MessageData md(topicName, message);
|
||||
defaultMessageHandler(md);
|
||||
rc = SUCCESS;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Network, class Timer, int a, int b>
|
||||
int MQTT::Client<Network, Timer, a, b>::yield(unsigned long timeout_ms)
|
||||
{
|
||||
int rc = SUCCESS;
|
||||
Timer timer = Timer();
|
||||
|
||||
timer.countdown_ms(timeout_ms);
|
||||
while (!timer.expired())
|
||||
{
|
||||
if (cycle(timer) < 0)
|
||||
{
|
||||
rc = FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::cycle(Timer& timer)
|
||||
{
|
||||
/* get one piece of work off the wire and one pass through */
|
||||
|
||||
// read the socket, see what work is due
|
||||
int packet_type = readPacket(timer);
|
||||
int rc = SUCCESS;
|
||||
if (packet_type == BUFFER_OVERFLOW) {
|
||||
MQTTHeader header = {0};
|
||||
header.byte = readbuf[0];
|
||||
packet_type = header.bits.type;
|
||||
}
|
||||
|
||||
int len = 0;
|
||||
switch (packet_type)
|
||||
{
|
||||
case FAILURE:
|
||||
case BUFFER_OVERFLOW:
|
||||
rc = packet_type;
|
||||
break;
|
||||
case CONNACK:
|
||||
case PUBACK:
|
||||
case SUBACK:
|
||||
break;
|
||||
case PUBLISH:
|
||||
{
|
||||
MQTTString topicName = MQTTString_initializer;
|
||||
Message msg;
|
||||
int intQoS;
|
||||
if (MQTTDeserialize_publish((unsigned char*)&msg.dup, &intQoS, (unsigned char*)&msg.retained, (unsigned short*)&msg.id, &topicName,
|
||||
(unsigned char**)&msg.payload, (int*)&msg.payloadlen, readbuf, MAX_MQTT_PACKET_SIZE) != 1)
|
||||
goto exit;
|
||||
msg.qos = (enum QoS)intQoS;
|
||||
#if MQTTCLIENT_QOS2
|
||||
if (msg.qos != QOS2)
|
||||
#endif
|
||||
deliverMessage(topicName, msg);
|
||||
#if MQTTCLIENT_QOS2
|
||||
else if (isQoS2msgidFree(msg.id))
|
||||
{
|
||||
if (useQoS2msgid(msg.id))
|
||||
deliverMessage(topicName, msg);
|
||||
else
|
||||
WARN("Maximum number of incoming QoS2 messages exceeded");
|
||||
}
|
||||
#endif
|
||||
#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
|
||||
if (msg.qos != QOS0)
|
||||
{
|
||||
if (msg.qos == QOS1)
|
||||
len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBACK, 0, msg.id);
|
||||
else if (msg.qos == QOS2)
|
||||
len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBREC, 0, msg.id);
|
||||
if (len <= 0)
|
||||
rc = FAILURE;
|
||||
else
|
||||
rc = sendPacket(len, timer);
|
||||
if (rc == FAILURE)
|
||||
goto exit; // there was a problem
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
#if MQTTCLIENT_QOS2
|
||||
case PUBREC:
|
||||
case PUBREL:
|
||||
unsigned short mypacketid;
|
||||
unsigned char dup, type;
|
||||
if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1)
|
||||
rc = FAILURE;
|
||||
else if ((len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE,
|
||||
(packet_type == PUBREC) ? PUBREL : PUBCOMP, 0, mypacketid)) <= 0)
|
||||
rc = FAILURE;
|
||||
else if ((rc = sendPacket(len, timer)) != SUCCESS) // send the PUBREL packet
|
||||
rc = FAILURE; // there was a problem
|
||||
if (rc == FAILURE)
|
||||
goto exit; // there was a problem
|
||||
if (packet_type == PUBREL)
|
||||
freeQoS2msgid(mypacketid);
|
||||
break;
|
||||
|
||||
case PUBCOMP:
|
||||
break;
|
||||
#endif
|
||||
case PINGRESP:
|
||||
ping_outstanding = false;
|
||||
break;
|
||||
}
|
||||
keepalive();
|
||||
exit:
|
||||
if (rc == SUCCESS)
|
||||
rc = packet_type;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::keepalive()
|
||||
{
|
||||
int rc = FAILURE;
|
||||
|
||||
if (keepAliveInterval == 0)
|
||||
{
|
||||
rc = SUCCESS;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (last_sent.expired() || last_received.expired())
|
||||
{
|
||||
if (!ping_outstanding)
|
||||
{
|
||||
Timer timer = Timer(1000);
|
||||
int len = MQTTSerialize_pingreq(sendbuf, MAX_MQTT_PACKET_SIZE);
|
||||
if (len > 0 && (rc = sendPacket(len, timer)) == SUCCESS) // send the ping packet
|
||||
ping_outstanding = true;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
// only used in single-threaded mode where one command at a time is in process
|
||||
template<class Network, class Timer, int a, int b>
|
||||
int MQTT::Client<Network, Timer, a, b>::waitfor(int packet_type, Timer& timer)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
|
||||
do
|
||||
{
|
||||
if (timer.expired())
|
||||
break; // we timed out
|
||||
}
|
||||
while ((rc = cycle(timer)) != packet_type);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::connect(MQTTPacket_connectData& options)
|
||||
{
|
||||
Timer connect_timer = Timer(command_timeout_ms);
|
||||
int rc = FAILURE;
|
||||
int len = 0;
|
||||
|
||||
if (isconnected) // don't send connect packet again if we are already connected
|
||||
goto exit;
|
||||
|
||||
this->keepAliveInterval = options.keepAliveInterval;
|
||||
this->cleansession = options.cleansession;
|
||||
if ((len = MQTTSerialize_connect(sendbuf, MAX_MQTT_PACKET_SIZE, &options)) <= 0)
|
||||
goto exit;
|
||||
if ((rc = sendPacket(len, connect_timer)) != SUCCESS) // send the connect packet
|
||||
goto exit; // there was a problem
|
||||
|
||||
if (this->keepAliveInterval > 0)
|
||||
last_received.countdown(this->keepAliveInterval);
|
||||
// this will be a blocking call, wait for the connack
|
||||
if (waitfor(CONNACK, connect_timer) == CONNACK)
|
||||
{
|
||||
unsigned char connack_rc = 255;
|
||||
bool sessionPresent = false;
|
||||
if (MQTTDeserialize_connack((unsigned char*)&sessionPresent, &connack_rc, readbuf, MAX_MQTT_PACKET_SIZE) == 1)
|
||||
rc = connack_rc;
|
||||
else
|
||||
rc = FAILURE;
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
|
||||
#if MQTTCLIENT_QOS2
|
||||
// resend any inflight publish
|
||||
if (inflightMsgid > 0 && inflightQoS == QOS2 && pubrel)
|
||||
{
|
||||
if ((len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBREL, 0, inflightMsgid)) <= 0)
|
||||
rc = FAILURE;
|
||||
else
|
||||
rc = publish(len, connect_timer, inflightQoS);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
|
||||
if (inflightMsgid > 0)
|
||||
{
|
||||
memcpy(sendbuf, pubbuf, MAX_MQTT_PACKET_SIZE);
|
||||
rc = publish(inflightLen, connect_timer, inflightQoS);
|
||||
}
|
||||
#endif
|
||||
|
||||
exit:
|
||||
if (rc == SUCCESS)
|
||||
isconnected = true;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::connect()
|
||||
{
|
||||
MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer;
|
||||
return connect(default_options);
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int MAX_MESSAGE_HANDLERS>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, MAX_MESSAGE_HANDLERS>::subscribe(const char* topicFilter, enum QoS qos, messageHandler messageHandler)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
Timer timer = Timer(command_timeout_ms);
|
||||
int len = 0;
|
||||
MQTTString topic = {(char*)topicFilter, {0, 0}};
|
||||
|
||||
if (!isconnected)
|
||||
goto exit;
|
||||
|
||||
len = MQTTSerialize_subscribe(sendbuf, MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic, (int*)&qos);
|
||||
if (len <= 0)
|
||||
goto exit;
|
||||
if ((rc = sendPacket(len, timer)) != SUCCESS) // send the subscribe packet
|
||||
goto exit; // there was a problem
|
||||
|
||||
if (waitfor(SUBACK, timer) == SUBACK) // wait for suback
|
||||
{
|
||||
int count = 0, grantedQoS = -1;
|
||||
unsigned short mypacketid;
|
||||
if (MQTTDeserialize_suback(&mypacketid, 1, &count, &grantedQoS, readbuf, MAX_MQTT_PACKET_SIZE) == 1)
|
||||
rc = grantedQoS; // 0, 1, 2 or 0x80
|
||||
if (rc != 0x80)
|
||||
{
|
||||
for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
|
||||
{
|
||||
if (messageHandlers[i].topicFilter == 0)
|
||||
{
|
||||
messageHandlers[i].topicFilter = topicFilter;
|
||||
messageHandlers[i].fp.attach(messageHandler);
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
|
||||
exit:
|
||||
if (rc != SUCCESS)
|
||||
cleanSession();
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int MAX_MESSAGE_HANDLERS>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, MAX_MESSAGE_HANDLERS>::unsubscribe(const char* topicFilter)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
Timer timer = Timer(command_timeout_ms);
|
||||
MQTTString topic = {(char*)topicFilter, {0, 0}};
|
||||
int len = 0;
|
||||
|
||||
if (!isconnected)
|
||||
goto exit;
|
||||
|
||||
if ((len = MQTTSerialize_unsubscribe(sendbuf, MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic)) <= 0)
|
||||
goto exit;
|
||||
if ((rc = sendPacket(len, timer)) != SUCCESS) // send the unsubscribe packet
|
||||
goto exit; // there was a problem
|
||||
|
||||
if (waitfor(UNSUBACK, timer) == UNSUBACK)
|
||||
{
|
||||
unsigned short mypacketid; // should be the same as the packetid above
|
||||
if (MQTTDeserialize_unsuback(&mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) == 1)
|
||||
{
|
||||
rc = 0;
|
||||
|
||||
// remove the subscription message handler associated with this topic, if there is one
|
||||
for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
|
||||
{
|
||||
if (strcmp(messageHandlers[i].topicFilter, topicFilter) == 0)
|
||||
{
|
||||
messageHandlers[i].topicFilter = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
|
||||
exit:
|
||||
if (rc != SUCCESS)
|
||||
cleanSession();
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(int len, Timer& timer, enum QoS qos)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if ((rc = sendPacket(len, timer)) != SUCCESS) // send the publish packet
|
||||
goto exit; // there was a problem
|
||||
|
||||
#if MQTTCLIENT_QOS1
|
||||
if (qos == QOS1)
|
||||
{
|
||||
if (waitfor(PUBACK, timer) == PUBACK)
|
||||
{
|
||||
unsigned short mypacketid;
|
||||
unsigned char dup, type;
|
||||
if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1)
|
||||
rc = FAILURE;
|
||||
else if (inflightMsgid == mypacketid)
|
||||
inflightMsgid = 0;
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
}
|
||||
#elif MQTTCLIENT_QOS2
|
||||
else if (qos == QOS2)
|
||||
{
|
||||
if (waitfor(PUBCOMP, timer) == PUBCOMP)
|
||||
{
|
||||
unsigned short mypacketid;
|
||||
unsigned char dup, type;
|
||||
if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1)
|
||||
rc = FAILURE;
|
||||
else if (inflightMsgid == mypacketid)
|
||||
inflightMsgid = 0;
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
exit:
|
||||
if (rc != SUCCESS)
|
||||
cleanSession();
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(const char* topicName, void* payload, size_t payloadlen, unsigned short& id, enum QoS qos, bool retained)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
Timer timer = Timer(command_timeout_ms);
|
||||
MQTTString topicString = MQTTString_initializer;
|
||||
int len = 0;
|
||||
|
||||
if (!isconnected)
|
||||
goto exit;
|
||||
|
||||
topicString.cstring = (char*)topicName;
|
||||
|
||||
#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
|
||||
if (qos == QOS1 || qos == QOS2)
|
||||
id = packetid.getNext();
|
||||
#endif
|
||||
|
||||
len = MQTTSerialize_publish(sendbuf, MAX_MQTT_PACKET_SIZE, 0, qos, retained, id,
|
||||
topicString, (unsigned char*)payload, payloadlen);
|
||||
if (len <= 0)
|
||||
goto exit;
|
||||
|
||||
#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
|
||||
if (!cleansession)
|
||||
{
|
||||
memcpy(pubbuf, sendbuf, len);
|
||||
inflightMsgid = id;
|
||||
inflightLen = len;
|
||||
inflightQoS = qos;
|
||||
#if MQTTCLIENT_QOS2
|
||||
pubrel = false;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = publish(len, timer, qos);
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(const char* topicName, void* payload, size_t payloadlen, enum QoS qos, bool retained)
|
||||
{
|
||||
unsigned short id = 0; // dummy - not used for anything
|
||||
return publish(topicName, payload, payloadlen, id, qos, retained);
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(const char* topicName, Message& message)
|
||||
{
|
||||
return publish(topicName, message.payload, message.payloadlen, message.qos, message.retained);
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::disconnect()
|
||||
{
|
||||
int rc = FAILURE;
|
||||
Timer timer = Timer(command_timeout_ms); // we might wait for incomplete incoming publishes to complete
|
||||
int len = MQTTSerialize_disconnect(sendbuf, MAX_MQTT_PACKET_SIZE);
|
||||
if (len > 0)
|
||||
rc = sendPacket(len, timer); // send the disconnect packet
|
||||
|
||||
if (cleansession)
|
||||
cleanSession();
|
||||
else
|
||||
isconnected = false;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
136
arduino/libraries/Temboo/src/utility/MQTTConnect.h
Normal file
136
arduino/libraries/Temboo/src/utility/MQTTConnect.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MQTTCONNECT_H_
|
||||
#define MQTTCONNECT_H_
|
||||
|
||||
#if !defined(DLLImport)
|
||||
#define DLLImport
|
||||
#endif
|
||||
#if !defined(DLLExport)
|
||||
#define DLLExport
|
||||
#endif
|
||||
|
||||
|
||||
typedef union
|
||||
{
|
||||
unsigned char all; /**< all connect flags */
|
||||
#if defined(REVERSED)
|
||||
struct
|
||||
{
|
||||
unsigned int username : 1; /**< 3.1 user name */
|
||||
unsigned int password : 1; /**< 3.1 password */
|
||||
unsigned int willRetain : 1; /**< will retain setting */
|
||||
unsigned int willQoS : 2; /**< will QoS value */
|
||||
unsigned int will : 1; /**< will flag */
|
||||
unsigned int cleansession : 1; /**< clean session flag */
|
||||
unsigned int : 1; /**< unused */
|
||||
} bits;
|
||||
#else
|
||||
struct
|
||||
{
|
||||
unsigned int : 1; /**< unused */
|
||||
unsigned int cleansession : 1; /**< cleansession flag */
|
||||
unsigned int will : 1; /**< will flag */
|
||||
unsigned int willQoS : 2; /**< will QoS value */
|
||||
unsigned int willRetain : 1; /**< will retain setting */
|
||||
unsigned int password : 1; /**< 3.1 password */
|
||||
unsigned int username : 1; /**< 3.1 user name */
|
||||
} bits;
|
||||
#endif
|
||||
} MQTTConnectFlags; /**< connect flags byte */
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Defines the MQTT "Last Will and Testament" (LWT) settings for
|
||||
* the connect packet.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. must be MQTW. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0 */
|
||||
int struct_version;
|
||||
/** The LWT topic to which the LWT message will be published. */
|
||||
MQTTString topicName;
|
||||
/** The LWT payload. */
|
||||
MQTTString message;
|
||||
/**
|
||||
* The retained flag for the LWT message (see MQTTAsync_message.retained).
|
||||
*/
|
||||
unsigned char retained;
|
||||
/**
|
||||
* The quality of service setting for the LWT message (see
|
||||
* MQTTAsync_message.qos and @ref qos).
|
||||
*/
|
||||
char qos;
|
||||
} MQTTPacket_willOptions;
|
||||
|
||||
|
||||
#define MQTTPacket_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 0, {NULL, {0, NULL}}, {NULL, {0, NULL}}, 0, 0 }
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. must be MQTC. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0 */
|
||||
int struct_version;
|
||||
/** Version of MQTT to be used. 3 = 3.1 4 = 3.1.1
|
||||
*/
|
||||
unsigned char MQTTVersion;
|
||||
MQTTString clientID;
|
||||
unsigned short keepAliveInterval;
|
||||
unsigned char cleansession;
|
||||
unsigned char willFlag;
|
||||
MQTTPacket_willOptions will;
|
||||
MQTTString username;
|
||||
MQTTString password;
|
||||
} MQTTPacket_connectData;
|
||||
|
||||
typedef union
|
||||
{
|
||||
unsigned char all; /**< all connack flags */
|
||||
#if defined(REVERSED)
|
||||
struct
|
||||
{
|
||||
unsigned int sessionpresent : 1; /**< session present flag */
|
||||
unsigned int : 7; /**< unused */
|
||||
} bits;
|
||||
#else
|
||||
struct
|
||||
{
|
||||
unsigned int : 7; /**< unused */
|
||||
unsigned int sessionpresent : 1; /**< session present flag */
|
||||
} bits;
|
||||
#endif
|
||||
} MQTTConnackFlags; /**< connack flags byte */
|
||||
|
||||
#define MQTTPacket_connectData_initializer { {'M', 'Q', 'T', 'C'}, 0, 4, {NULL, {0, NULL}}, 60, 1, 0, \
|
||||
MQTTPacket_willOptions_initializer, {NULL, {0, NULL}}, {NULL, {0, NULL}} }
|
||||
|
||||
DLLExport int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options);
|
||||
DLLExport int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len);
|
||||
|
||||
DLLExport int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent);
|
||||
DLLExport int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen);
|
||||
|
||||
DLLExport int MQTTSerialize_disconnect(unsigned char* buf, int buflen);
|
||||
DLLExport int MQTTSerialize_pingreq(unsigned char* buf, int buflen);
|
||||
|
||||
#endif /* MQTTCONNECT_H_ */
|
||||
214
arduino/libraries/Temboo/src/utility/MQTTConnectClient.c
Normal file
214
arduino/libraries/Temboo/src/utility/MQTTConnectClient.c
Normal file
@@ -0,0 +1,214 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Determines the length of the MQTT connect packet that would be produced using the supplied connect options.
|
||||
* @param options the options to be used to build the connect packet
|
||||
* @return the length of buffer needed to contain the serialized version of the packet
|
||||
*/
|
||||
int MQTTSerialize_connectLength(MQTTPacket_connectData* options)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
|
||||
if (options->MQTTVersion == 3)
|
||||
len = 12; /* variable depending on MQTT or MQIsdp */
|
||||
else if (options->MQTTVersion == 4)
|
||||
len = 10;
|
||||
|
||||
len += MQTTstrlen(options->clientID)+2;
|
||||
if (options->willFlag)
|
||||
len += MQTTstrlen(options->will.topicName)+2 + MQTTstrlen(options->will.message)+2;
|
||||
if (options->username.cstring || options->username.lenstring.data)
|
||||
len += MQTTstrlen(options->username)+2;
|
||||
if (options->password.cstring || options->password.lenstring.data)
|
||||
len += MQTTstrlen(options->password)+2;
|
||||
|
||||
FUNC_EXIT_RC(len);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the connect options into the buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param len the length in bytes of the supplied buffer
|
||||
* @param options the options to be used to build the connect packet
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options)
|
||||
{
|
||||
unsigned char *ptr = buf;
|
||||
MQTTHeader header = {0};
|
||||
MQTTConnectFlags flags = {0};
|
||||
int len = 0;
|
||||
int rc = -1;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (MQTTPacket_len(len = MQTTSerialize_connectLength(options)) > buflen)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
header.byte = 0;
|
||||
header.bits.type = CONNECT;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, len); /* write remaining length */
|
||||
|
||||
if (options->MQTTVersion == 4)
|
||||
{
|
||||
writeCString(&ptr, "MQTT");
|
||||
writeChar(&ptr, (char) 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeCString(&ptr, "MQIsdp");
|
||||
writeChar(&ptr, (char) 3);
|
||||
}
|
||||
|
||||
flags.all = 0;
|
||||
flags.bits.cleansession = options->cleansession;
|
||||
flags.bits.will = (options->willFlag) ? 1 : 0;
|
||||
if (flags.bits.will)
|
||||
{
|
||||
flags.bits.willQoS = options->will.qos;
|
||||
flags.bits.willRetain = options->will.retained;
|
||||
}
|
||||
|
||||
if (options->username.cstring || options->username.lenstring.data)
|
||||
flags.bits.username = 1;
|
||||
if (options->password.cstring || options->password.lenstring.data)
|
||||
flags.bits.password = 1;
|
||||
|
||||
writeChar(&ptr, flags.all);
|
||||
writeInt(&ptr, options->keepAliveInterval);
|
||||
writeMQTTString(&ptr, options->clientID);
|
||||
if (options->willFlag)
|
||||
{
|
||||
writeMQTTString(&ptr, options->will.topicName);
|
||||
writeMQTTString(&ptr, options->will.message);
|
||||
}
|
||||
if (flags.bits.username)
|
||||
writeMQTTString(&ptr, options->username);
|
||||
if (flags.bits.password)
|
||||
writeMQTTString(&ptr, options->password);
|
||||
|
||||
rc = ptr - buf;
|
||||
|
||||
exit: FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into connack data - return code
|
||||
* @param sessionPresent the session present flag returned (only for MQTT 3.1.1)
|
||||
* @param connack_rc returned integer value of the connack return code
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param len the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success, 0 is failure
|
||||
*/
|
||||
int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = 0;
|
||||
int mylen;
|
||||
MQTTConnackFlags flags = {0};
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != CONNACK)
|
||||
goto exit;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
if (enddata - curdata < 2)
|
||||
goto exit;
|
||||
|
||||
flags.all = readChar(&curdata);
|
||||
*sessionPresent = flags.bits.sessionpresent;
|
||||
*connack_rc = readChar(&curdata);
|
||||
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a 0-length packet into the supplied buffer, ready for writing to a socket
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer, to avoid overruns
|
||||
* @param packettype the message type
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_zero(unsigned char* buf, int buflen, unsigned char packettype)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
int rc = -1;
|
||||
unsigned char *ptr = buf;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (buflen < 2)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
header.byte = 0;
|
||||
header.bits.type = packettype;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, 0); /* write remaining length */
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a disconnect packet into the supplied buffer, ready for writing to a socket
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer, to avoid overruns
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_disconnect(unsigned char* buf, int buflen)
|
||||
{
|
||||
return MQTTSerialize_zero(buf, buflen, DISCONNECT);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a disconnect packet into the supplied buffer, ready for writing to a socket
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer, to avoid overruns
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_pingreq(unsigned char* buf, int buflen)
|
||||
{
|
||||
return MQTTSerialize_zero(buf, buflen, PINGREQ);
|
||||
}
|
||||
148
arduino/libraries/Temboo/src/utility/MQTTConnectServer.c
Normal file
148
arduino/libraries/Temboo/src/utility/MQTTConnectServer.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "StackTrace.h"
|
||||
#include "MQTTPacket.h"
|
||||
#include <string.h>
|
||||
|
||||
#define min(a, b) ((a < b) ? a : b)
|
||||
|
||||
|
||||
/**
|
||||
* Validates MQTT protocol name and version combinations
|
||||
* @param protocol the MQTT protocol name as an MQTTString
|
||||
* @param version the MQTT protocol version number, as in the connect packet
|
||||
* @return correct MQTT combination? 1 is true, 0 is false
|
||||
*/
|
||||
int MQTTPacket_checkVersion(MQTTString* protocol, int version)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (version == 3 && memcmp(protocol->lenstring.data, "MQIsdp",
|
||||
min(6, protocol->lenstring.len)) == 0)
|
||||
rc = 1;
|
||||
else if (version == 4 && memcmp(protocol->lenstring.data, "MQTT",
|
||||
min(4, protocol->lenstring.len)) == 0)
|
||||
rc = 1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into connect data structure
|
||||
* @param data the connect data structure to be filled out
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param len the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success, 0 is failure
|
||||
*/
|
||||
int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
MQTTConnectFlags flags = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = &buf[len];
|
||||
int rc = 0;
|
||||
MQTTString Protocol;
|
||||
int version;
|
||||
int mylen = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != CONNECT)
|
||||
goto exit;
|
||||
|
||||
curdata += MQTTPacket_decodeBuf(curdata, &mylen); /* read remaining length */
|
||||
|
||||
if (!readMQTTLenString(&Protocol, &curdata, enddata) ||
|
||||
enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */
|
||||
goto exit;
|
||||
|
||||
version = (int)readChar(&curdata); /* Protocol version */
|
||||
/* If we don't recognize the protocol version, we don't parse the connect packet on the
|
||||
* basis that we don't know what the format will be.
|
||||
*/
|
||||
if (MQTTPacket_checkVersion(&Protocol, version))
|
||||
{
|
||||
flags.all = readChar(&curdata);
|
||||
data->cleansession = flags.bits.cleansession;
|
||||
data->keepAliveInterval = readInt(&curdata);
|
||||
if (!readMQTTLenString(&data->clientID, &curdata, enddata))
|
||||
goto exit;
|
||||
data->willFlag = flags.bits.will;
|
||||
if (flags.bits.will)
|
||||
{
|
||||
data->will.qos = flags.bits.willQoS;
|
||||
data->will.retained = flags.bits.willRetain;
|
||||
if (!readMQTTLenString(&data->will.topicName, &curdata, enddata) ||
|
||||
!readMQTTLenString(&data->will.message, &curdata, enddata))
|
||||
goto exit;
|
||||
}
|
||||
if (flags.bits.username)
|
||||
{
|
||||
if (enddata - curdata < 3 || !readMQTTLenString(&data->username, &curdata, enddata))
|
||||
goto exit; /* username flag set, but no username supplied - invalid */
|
||||
if (flags.bits.password &&
|
||||
(enddata - curdata < 3 || !readMQTTLenString(&data->password, &curdata, enddata)))
|
||||
goto exit; /* password flag set, but no password supplied - invalid */
|
||||
}
|
||||
else if (flags.bits.password)
|
||||
goto exit; /* password flag set without username - invalid */
|
||||
rc = 1;
|
||||
}
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the connack packet into the supplied buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param connack_rc the integer connack return code to be used
|
||||
* @param sessionPresent the MQTT 3.1.1 sessionPresent flag
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
int rc = 0;
|
||||
unsigned char *ptr = buf;
|
||||
MQTTConnackFlags flags = {0};
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (buflen < 2)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
header.byte = 0;
|
||||
header.bits.type = CONNACK;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */
|
||||
|
||||
flags.all = 0;
|
||||
flags.bits.sessionpresent = sessionPresent;
|
||||
writeChar(&ptr, flags.all);
|
||||
writeChar(&ptr, connack_rc);
|
||||
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
107
arduino/libraries/Temboo/src/utility/MQTTDeserializePublish.c
Normal file
107
arduino/libraries/Temboo/src/utility/MQTTDeserializePublish.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "StackTrace.h"
|
||||
#include "MQTTPacket.h"
|
||||
#include <string.h>
|
||||
|
||||
#define min(a, b) ((a < b) ? 1 : 0)
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into publish data
|
||||
* @param dup returned integer - the MQTT dup flag
|
||||
* @param qos returned integer - the MQTT QoS value
|
||||
* @param retained returned integer - the MQTT retained flag
|
||||
* @param packetid returned integer - the MQTT packet identifier
|
||||
* @param topicName returned MQTTString - the MQTT topic in the publish
|
||||
* @param payload returned byte buffer - the MQTT publish payload
|
||||
* @param payloadlen returned integer - the length of the MQTT payload
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success
|
||||
*/
|
||||
int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName,
|
||||
unsigned char** payload, int* payloadlen, unsigned char* buf, int buflen)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = 0;
|
||||
int mylen = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != PUBLISH)
|
||||
goto exit;
|
||||
*dup = header.bits.dup;
|
||||
*qos = header.bits.qos;
|
||||
*retained = header.bits.retain;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
|
||||
if (!readMQTTLenString(topicName, &curdata, enddata) ||
|
||||
enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */
|
||||
goto exit;
|
||||
|
||||
if (*qos > 0)
|
||||
*packetid = readInt(&curdata);
|
||||
|
||||
*payloadlen = enddata - curdata;
|
||||
*payload = curdata;
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into an ack
|
||||
* @param packettype returned integer - the MQTT packet type
|
||||
* @param dup returned integer - the MQTT dup flag
|
||||
* @param packetid returned integer - the MQTT packet identifier
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success, 0 is failure
|
||||
*/
|
||||
int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = 0;
|
||||
int mylen;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
*dup = header.bits.dup;
|
||||
*packettype = header.bits.type;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
|
||||
if (enddata - curdata < 2)
|
||||
goto exit;
|
||||
*packetid = readInt(&curdata);
|
||||
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
256
arduino/libraries/Temboo/src/utility/MQTTFormat.c
Normal file
256
arduino/libraries/Temboo/src/utility/MQTTFormat.c
Normal file
@@ -0,0 +1,256 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "StackTrace.h"
|
||||
#include "MQTTPacket.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
const char* MQTTPacket_names[] =
|
||||
{
|
||||
"RESERVED", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC", "PUBREL",
|
||||
"PUBCOMP", "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK",
|
||||
"PINGREQ", "PINGRESP", "DISCONNECT"
|
||||
};
|
||||
|
||||
|
||||
const char* MQTTPacket_getName(unsigned short packetid)
|
||||
{
|
||||
return MQTTPacket_names[packetid];
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data)
|
||||
{
|
||||
int strindex = 0;
|
||||
|
||||
strindex = snprintf(strbuf, strbuflen,
|
||||
"CONNECT MQTT version %d, client id %.*s, clean session %d, keep alive %d",
|
||||
(int)data->MQTTVersion, data->clientID.lenstring.len, data->clientID.lenstring.data,
|
||||
(int)data->cleansession, data->keepAliveInterval);
|
||||
if (data->willFlag)
|
||||
strindex += snprintf(&strbuf[strindex], strbuflen - strindex,
|
||||
", will QoS %d, will retain %d, will topic %.*s, will message %.*s",
|
||||
data->will.qos, data->will.retained,
|
||||
data->will.topicName.lenstring.len, data->will.topicName.lenstring.data,
|
||||
data->will.message.lenstring.len, data->will.message.lenstring.data);
|
||||
if (data->username.lenstring.data && data->username.lenstring.len > 0)
|
||||
strindex += snprintf(&strbuf[strindex], strbuflen - strindex,
|
||||
", user name %.*s", data->username.lenstring.len, data->username.lenstring.data);
|
||||
if (data->password.lenstring.data && data->password.lenstring.len > 0)
|
||||
strindex += snprintf(&strbuf[strindex], strbuflen - strindex,
|
||||
", password %.*s", data->password.lenstring.len, data->password.lenstring.data);
|
||||
return strindex;
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent)
|
||||
{
|
||||
int strindex = snprintf(strbuf, strbuflen, "CONNACK session present %d, rc %d", sessionPresent, connack_rc);
|
||||
return strindex;
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained,
|
||||
unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen)
|
||||
{
|
||||
int strindex = snprintf(strbuf, strbuflen,
|
||||
"PUBLISH dup %d, QoS %d, retained %d, packet id %d, topic %.*s, payload length %d, payload %.*s",
|
||||
dup, qos, retained, packetid,
|
||||
(topicName.lenstring.len < 20) ? topicName.lenstring.len : 20, topicName.lenstring.data,
|
||||
payloadlen, (payloadlen < 20) ? payloadlen : 20, payload);
|
||||
return strindex;
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid)
|
||||
{
|
||||
int strindex = snprintf(strbuf, strbuflen, "%s, packet id %d", MQTTPacket_names[packettype], packetid);
|
||||
if (dup)
|
||||
strindex += snprintf(strbuf + strindex, strbuflen - strindex, ", dup %d", dup);
|
||||
return strindex;
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count,
|
||||
MQTTString topicFilters[], int requestedQoSs[])
|
||||
{
|
||||
return snprintf(strbuf, strbuflen,
|
||||
"SUBSCRIBE dup %d, packet id %d count %d topic %.*s qos %d",
|
||||
dup, packetid, count,
|
||||
topicFilters[0].lenstring.len, topicFilters[0].lenstring.data,
|
||||
requestedQoSs[0]);
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs)
|
||||
{
|
||||
return snprintf(strbuf, strbuflen,
|
||||
"SUBACK packet id %d count %d granted qos %d", packetid, count, grantedQoSs[0]);
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid,
|
||||
int count, MQTTString topicFilters[])
|
||||
{
|
||||
return snprintf(strbuf, strbuflen,
|
||||
"UNSUBSCRIBE dup %d, packet id %d count %d topic %.*s",
|
||||
dup, packetid, count,
|
||||
topicFilters[0].lenstring.len, topicFilters[0].lenstring.data);
|
||||
}
|
||||
|
||||
|
||||
char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen)
|
||||
{
|
||||
int index = 0;
|
||||
int rem_length = 0;
|
||||
MQTTHeader header = {0};
|
||||
|
||||
header.byte = buf[index++];
|
||||
index += MQTTPacket_decodeBuf(&buf[index], &rem_length);
|
||||
|
||||
switch (header.bits.type)
|
||||
{
|
||||
case CONNACK:
|
||||
{
|
||||
unsigned char sessionPresent, connack_rc;
|
||||
if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) == 1)
|
||||
MQTTStringFormat_connack(strbuf, strbuflen, connack_rc, sessionPresent);
|
||||
}
|
||||
break;
|
||||
case PUBLISH:
|
||||
{
|
||||
unsigned char dup, retained, *payload;
|
||||
unsigned short packetid;
|
||||
int qos, payloadlen;
|
||||
MQTTString topicName = MQTTString_initializer;
|
||||
if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName,
|
||||
&payload, &payloadlen, buf, buflen) == 1)
|
||||
MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid,
|
||||
topicName, payload, payloadlen);
|
||||
}
|
||||
break;
|
||||
case PUBACK:
|
||||
case PUBREC:
|
||||
case PUBREL:
|
||||
case PUBCOMP:
|
||||
{
|
||||
unsigned char packettype, dup;
|
||||
unsigned short packetid;
|
||||
if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1)
|
||||
MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid);
|
||||
}
|
||||
break;
|
||||
case SUBACK:
|
||||
{
|
||||
unsigned short packetid;
|
||||
int maxcount = 1, count = 0;
|
||||
int grantedQoSs[1];
|
||||
if (MQTTDeserialize_suback(&packetid, maxcount, &count, grantedQoSs, buf, buflen) == 1)
|
||||
MQTTStringFormat_suback(strbuf, strbuflen, packetid, count, grantedQoSs);
|
||||
}
|
||||
break;
|
||||
case UNSUBACK:
|
||||
{
|
||||
unsigned short packetid;
|
||||
if (MQTTDeserialize_unsuback(&packetid, buf, buflen) == 1)
|
||||
MQTTStringFormat_ack(strbuf, strbuflen, UNSUBACK, 0, packetid);
|
||||
}
|
||||
break;
|
||||
case PINGREQ:
|
||||
case PINGRESP:
|
||||
case DISCONNECT:
|
||||
snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]);
|
||||
break;
|
||||
}
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
|
||||
char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen)
|
||||
{
|
||||
int index = 0;
|
||||
int rem_length = 0;
|
||||
MQTTHeader header = {0};
|
||||
|
||||
header.byte = buf[index++];
|
||||
index += MQTTPacket_decodeBuf(&buf[index], &rem_length);
|
||||
|
||||
switch (header.bits.type)
|
||||
{
|
||||
case CONNECT:
|
||||
{
|
||||
MQTTPacket_connectData data;
|
||||
int rc;
|
||||
if ((rc = MQTTDeserialize_connect(&data, buf, buflen)) == 1)
|
||||
MQTTStringFormat_connect(strbuf, strbuflen, &data);
|
||||
}
|
||||
break;
|
||||
case PUBLISH:
|
||||
{
|
||||
unsigned char dup, retained, *payload;
|
||||
unsigned short packetid;
|
||||
int qos, payloadlen;
|
||||
MQTTString topicName = MQTTString_initializer;
|
||||
if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName,
|
||||
&payload, &payloadlen, buf, buflen) == 1)
|
||||
MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid,
|
||||
topicName, payload, payloadlen);
|
||||
}
|
||||
break;
|
||||
case PUBACK:
|
||||
case PUBREC:
|
||||
case PUBREL:
|
||||
case PUBCOMP:
|
||||
{
|
||||
unsigned char packettype, dup;
|
||||
unsigned short packetid;
|
||||
if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1)
|
||||
MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid);
|
||||
}
|
||||
break;
|
||||
case SUBSCRIBE:
|
||||
{
|
||||
unsigned char dup;
|
||||
unsigned short packetid;
|
||||
int maxcount = 1, count = 0;
|
||||
MQTTString topicFilters[1];
|
||||
int requestedQoSs[1];
|
||||
if (MQTTDeserialize_subscribe(&dup, &packetid, maxcount, &count,
|
||||
topicFilters, requestedQoSs, buf, buflen) == 1)
|
||||
MQTTStringFormat_subscribe(strbuf, strbuflen, dup, packetid, count, topicFilters, requestedQoSs);;
|
||||
}
|
||||
break;
|
||||
case UNSUBSCRIBE:
|
||||
{
|
||||
unsigned char dup;
|
||||
unsigned short packetid;
|
||||
int maxcount = 1, count = 0;
|
||||
MQTTString topicFilters[1];
|
||||
if (MQTTDeserialize_unsubscribe(&dup, &packetid, maxcount, &count, topicFilters, buf, buflen) == 1)
|
||||
MQTTStringFormat_unsubscribe(strbuf, strbuflen, dup, packetid, count, topicFilters);
|
||||
}
|
||||
break;
|
||||
case PINGREQ:
|
||||
case PINGRESP:
|
||||
case DISCONNECT:
|
||||
snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]);
|
||||
break;
|
||||
}
|
||||
strbuf[strbuflen] = '\0';
|
||||
return strbuf;
|
||||
}
|
||||
37
arduino/libraries/Temboo/src/utility/MQTTFormat.h
Normal file
37
arduino/libraries/Temboo/src/utility/MQTTFormat.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTFORMAT_H)
|
||||
#define MQTTFORMAT_H
|
||||
|
||||
#include "StackTrace.h"
|
||||
#include "MQTTPacket.h"
|
||||
|
||||
const char* MQTTPacket_getName(unsigned short packetid);
|
||||
int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data);
|
||||
int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent);
|
||||
int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained,
|
||||
unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen);
|
||||
int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid);
|
||||
int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count,
|
||||
MQTTString topicFilters[], int requestedQoSs[]);
|
||||
int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs);
|
||||
int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid,
|
||||
int count, MQTTString topicFilters[]);
|
||||
char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen);
|
||||
char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen);
|
||||
|
||||
#endif
|
||||
41
arduino/libraries/Temboo/src/utility/MQTTLogging.h
Normal file
41
arduino/libraries/Temboo/src/utility/MQTTLogging.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
* Modified for Arduino environment by Temboo
|
||||
* Joe Planisky - replaced fancy fprintf stuff with Serial.print(ln)
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTT_LOGGING_H)
|
||||
#define MQTT_LOGGING_H
|
||||
|
||||
#if !defined(DEBUG)
|
||||
#define DEBUG(msg) {Serial.print("DEBUG: "); Serial.println(msg);}
|
||||
#endif
|
||||
|
||||
#if !defined(LOG)
|
||||
#define LOG(msg) {Serial.print("LOG: "); Serial.println(msg);}
|
||||
#endif
|
||||
|
||||
#if !defined(WARN)
|
||||
#define WARN(msg) {Serial.print("WARN: "); Serial.println(msg);}
|
||||
#endif
|
||||
|
||||
#if !defined(ERROR)
|
||||
#define ERROR() {Serial.print("ERROR: "); Serial.println(msg);}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
410
arduino/libraries/Temboo/src/utility/MQTTPacket.c
Normal file
410
arduino/libraries/Temboo/src/utility/MQTTPacket.c
Normal file
@@ -0,0 +1,410 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Sergio R. Caprile - non-blocking packet read functions for stream transport
|
||||
*******************************************************************************/
|
||||
|
||||
#include "StackTrace.h"
|
||||
#include "MQTTPacket.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Encodes the message length according to the MQTT algorithm
|
||||
* @param buf the buffer into which the encoded data is written
|
||||
* @param length the length to be encoded
|
||||
* @return the number of bytes written to buffer
|
||||
*/
|
||||
int MQTTPacket_encode(unsigned char* buf, int length)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
do
|
||||
{
|
||||
char d = length % 128;
|
||||
length /= 128;
|
||||
/* if there are more digits to encode, set the top bit of this digit */
|
||||
if (length > 0)
|
||||
d |= 0x80;
|
||||
buf[rc++] = d;
|
||||
} while (length > 0);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decodes the message length according to the MQTT algorithm
|
||||
* @param getcharfn pointer to function to read the next character from the data source
|
||||
* @param value the decoded length returned
|
||||
* @return the number of bytes read from the socket
|
||||
*/
|
||||
int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value)
|
||||
{
|
||||
unsigned char c;
|
||||
int multiplier = 1;
|
||||
int len = 0;
|
||||
#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
|
||||
|
||||
FUNC_ENTRY;
|
||||
*value = 0;
|
||||
do
|
||||
{
|
||||
int rc = MQTTPACKET_READ_ERROR;
|
||||
|
||||
if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
|
||||
{
|
||||
rc = MQTTPACKET_READ_ERROR; /* bad data */
|
||||
goto exit;
|
||||
}
|
||||
rc = (*getcharfn)(&c, 1);
|
||||
if (rc != 1)
|
||||
goto exit;
|
||||
*value += (c & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
} while ((c & 128) != 0);
|
||||
exit:
|
||||
FUNC_EXIT_RC(len);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
int MQTTPacket_len(int rem_len)
|
||||
{
|
||||
rem_len += 1; /* header byte */
|
||||
|
||||
/* now remaining_length field */
|
||||
if (rem_len < 128)
|
||||
rem_len += 1;
|
||||
else if (rem_len < 16384)
|
||||
rem_len += 2;
|
||||
else if (rem_len < 2097151)
|
||||
rem_len += 3;
|
||||
else
|
||||
rem_len += 4;
|
||||
return rem_len;
|
||||
}
|
||||
|
||||
|
||||
static unsigned char* bufptr;
|
||||
|
||||
int bufchar(unsigned char* c, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
*c = *bufptr++;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int MQTTPacket_decodeBuf(unsigned char* buf, int* value)
|
||||
{
|
||||
bufptr = buf;
|
||||
return MQTTPacket_decode(bufchar, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates an integer from two bytes read from the input buffer
|
||||
* @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @return the integer value calculated
|
||||
*/
|
||||
int readInt(unsigned char** pptr)
|
||||
{
|
||||
unsigned char* ptr = *pptr;
|
||||
int len = 256*(*ptr) + (*(ptr+1));
|
||||
*pptr += 2;
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads one character from the input buffer.
|
||||
* @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @return the character read
|
||||
*/
|
||||
char readChar(unsigned char** pptr)
|
||||
{
|
||||
char c = **pptr;
|
||||
(*pptr)++;
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes one character to an output buffer.
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param c the character to write
|
||||
*/
|
||||
void writeChar(unsigned char** pptr, char c)
|
||||
{
|
||||
**pptr = c;
|
||||
(*pptr)++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes an integer as 2 bytes to an output buffer.
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param anInt the integer to write
|
||||
*/
|
||||
void writeInt(unsigned char** pptr, int anInt)
|
||||
{
|
||||
**pptr = (unsigned char)(anInt / 256);
|
||||
(*pptr)++;
|
||||
**pptr = (unsigned char)(anInt % 256);
|
||||
(*pptr)++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes a "UTF" string to an output buffer. Converts C string to length-delimited.
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param string the C string to write
|
||||
*/
|
||||
void writeCString(unsigned char** pptr, const char* string)
|
||||
{
|
||||
int len = strlen(string);
|
||||
writeInt(pptr, len);
|
||||
memcpy(*pptr, string, len);
|
||||
*pptr += len;
|
||||
}
|
||||
|
||||
|
||||
int getLenStringLen(char* ptr)
|
||||
{
|
||||
int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1));
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
void writeMQTTString(unsigned char** pptr, MQTTString mqttstring)
|
||||
{
|
||||
if (mqttstring.lenstring.len > 0)
|
||||
{
|
||||
writeInt(pptr, mqttstring.lenstring.len);
|
||||
memcpy(*pptr, mqttstring.lenstring.data, mqttstring.lenstring.len);
|
||||
*pptr += mqttstring.lenstring.len;
|
||||
}
|
||||
else if (mqttstring.cstring)
|
||||
writeCString(pptr, mqttstring.cstring);
|
||||
else
|
||||
writeInt(pptr, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param mqttstring the MQTTString structure into which the data is to be read
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param enddata pointer to the end of the data: do not read beyond
|
||||
* @return 1 if successful, 0 if not
|
||||
*/
|
||||
int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
/* the first two bytes are the length of the string */
|
||||
if (enddata - (*pptr) > 1) /* enough length to read the integer? */
|
||||
{
|
||||
mqttstring->lenstring.len = readInt(pptr); /* increments pptr to point past length */
|
||||
if (&(*pptr)[mqttstring->lenstring.len] <= enddata)
|
||||
{
|
||||
mqttstring->lenstring.data = (char*)*pptr;
|
||||
*pptr += mqttstring->lenstring.len;
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
mqttstring->cstring = NULL;
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the length of the MQTTstring - C string if there is one, otherwise the length delimited string
|
||||
* @param mqttstring the string to return the length of
|
||||
* @return the length of the string
|
||||
*/
|
||||
int MQTTstrlen(MQTTString mqttstring)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (mqttstring.cstring)
|
||||
rc = strlen(mqttstring.cstring);
|
||||
else
|
||||
rc = mqttstring.lenstring.len;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares an MQTTString to a C string
|
||||
* @param a the MQTTString to compare
|
||||
* @param bptr the C string to compare
|
||||
* @return boolean - equal or not
|
||||
*/
|
||||
int MQTTPacket_equals(MQTTString* a, char* bptr)
|
||||
{
|
||||
int alen = 0,
|
||||
blen = 0;
|
||||
char *aptr;
|
||||
|
||||
if (a->cstring)
|
||||
{
|
||||
aptr = a->cstring;
|
||||
alen = strlen(a->cstring);
|
||||
}
|
||||
else
|
||||
{
|
||||
aptr = a->lenstring.data;
|
||||
alen = a->lenstring.len;
|
||||
}
|
||||
blen = strlen(bptr);
|
||||
|
||||
return (alen == blen) && (strncmp(aptr, bptr, alen) == 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to read packet data from some source into a buffer
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param getfn pointer to a function which will read any number of bytes from the needed source
|
||||
* @return integer MQTT packet type, or -1 on error
|
||||
* @note the whole message must fit into the caller's buffer
|
||||
*/
|
||||
int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int))
|
||||
{
|
||||
int rc = -1;
|
||||
MQTTHeader header = {0};
|
||||
int len = 0;
|
||||
int rem_len = 0;
|
||||
|
||||
/* 1. read the header byte. This has the packet type in it */
|
||||
if ((*getfn)(buf, 1) != 1)
|
||||
goto exit;
|
||||
|
||||
len = 1;
|
||||
/* 2. read the remaining length. This is variable in itself */
|
||||
MQTTPacket_decode(getfn, &rem_len);
|
||||
len += MQTTPacket_encode(buf + 1, rem_len); /* put the original remaining length back into the buffer */
|
||||
|
||||
/* 3. read the rest of the buffer using a callback to supply the rest of the data */
|
||||
if((rem_len + len) > buflen)
|
||||
goto exit;
|
||||
if ((*getfn)(buf + len, rem_len) != rem_len)
|
||||
goto exit;
|
||||
|
||||
header.byte = buf[0];
|
||||
rc = header.bits.type;
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the message length according to the MQTT algorithm, non-blocking
|
||||
* @param trp pointer to a transport structure holding what is needed to solve getting data from it
|
||||
* @param value the decoded length returned
|
||||
* @return integer the number of bytes read from the socket, 0 for call again, or -1 on error
|
||||
*/
|
||||
static int MQTTPacket_decodenb(MQTTTransport *trp)
|
||||
{
|
||||
unsigned char c;
|
||||
int rc = MQTTPACKET_READ_ERROR;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if(trp->len == 0){ /* initialize on first call */
|
||||
trp->multiplier = 1;
|
||||
trp->rem_len = 0;
|
||||
}
|
||||
do {
|
||||
int frc;
|
||||
if (++(trp->len) > MAX_NO_OF_REMAINING_LENGTH_BYTES)
|
||||
goto exit;
|
||||
if ((frc=(*trp->getfn)(trp->sck, &c, 1)) == -1)
|
||||
goto exit;
|
||||
if (frc == 0){
|
||||
rc = 0;
|
||||
goto exit;
|
||||
}
|
||||
trp->rem_len += (c & 127) * trp->multiplier;
|
||||
trp->multiplier *= 128;
|
||||
} while ((c & 128) != 0);
|
||||
rc = trp->len;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to read packet data from some source into a buffer, non-blocking
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param trp pointer to a transport structure holding what is needed to solve getting data from it
|
||||
* @return integer MQTT packet type, 0 for call again, or -1 on error
|
||||
* @note the whole message must fit into the caller's buffer
|
||||
*/
|
||||
int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp)
|
||||
{
|
||||
int rc = -1, frc;
|
||||
MQTTHeader header = {0};
|
||||
|
||||
switch(trp->state){
|
||||
default:
|
||||
trp->state = 0;
|
||||
/*FALLTHROUGH*/
|
||||
case 0:
|
||||
/* read the header byte. This has the packet type in it */
|
||||
if ((frc=(*trp->getfn)(trp->sck, buf, 1)) == -1)
|
||||
goto exit;
|
||||
if (frc == 0)
|
||||
return 0;
|
||||
trp->len = 0;
|
||||
++trp->state;
|
||||
/*FALLTHROUGH*/
|
||||
/* read the remaining length. This is variable in itself */
|
||||
case 1:
|
||||
if((frc=MQTTPacket_decodenb(trp)) == MQTTPACKET_READ_ERROR)
|
||||
goto exit;
|
||||
if(frc == 0)
|
||||
return 0;
|
||||
trp->len = 1 + MQTTPacket_encode(buf + 1, trp->rem_len); /* put the original remaining length back into the buffer */
|
||||
if((trp->rem_len + trp->len) > buflen)
|
||||
goto exit;
|
||||
++trp->state;
|
||||
/*FALLTHROUGH*/
|
||||
case 2:
|
||||
/* read the rest of the buffer using a callback to supply the rest of the data */
|
||||
if ((frc=(*trp->getfn)(trp->sck, buf + trp->len, trp->rem_len)) == -1)
|
||||
goto exit;
|
||||
if (frc == 0)
|
||||
return 0;
|
||||
trp->rem_len -= frc;
|
||||
trp->len += frc;
|
||||
if(trp->rem_len)
|
||||
return 0;
|
||||
|
||||
header.byte = buf[0];
|
||||
rc = header.bits.type;
|
||||
break;
|
||||
}
|
||||
|
||||
exit:
|
||||
trp->state = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
133
arduino/libraries/Temboo/src/utility/MQTTPacket.h
Normal file
133
arduino/libraries/Temboo/src/utility/MQTTPacket.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MQTTPACKET_H_
|
||||
#define MQTTPACKET_H_
|
||||
|
||||
#if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(WIN32_DLL) || defined(WIN64_DLL)
|
||||
#define DLLImport __declspec(dllimport)
|
||||
#define DLLExport __declspec(dllexport)
|
||||
#elif defined(LINUX_SO)
|
||||
#define DLLImport extern
|
||||
#define DLLExport __attribute__ ((visibility ("default")))
|
||||
#else
|
||||
#define DLLImport
|
||||
#define DLLExport
|
||||
#endif
|
||||
|
||||
enum errors
|
||||
{
|
||||
MQTTPACKET_BUFFER_TOO_SHORT = -2,
|
||||
MQTTPACKET_READ_ERROR = -1,
|
||||
MQTTPACKET_READ_COMPLETE
|
||||
};
|
||||
|
||||
enum msgTypes
|
||||
{
|
||||
CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL,
|
||||
PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK,
|
||||
PINGREQ, PINGRESP, DISCONNECT
|
||||
};
|
||||
|
||||
/**
|
||||
* Bitfields for the MQTT header byte.
|
||||
*/
|
||||
typedef union
|
||||
{
|
||||
unsigned char byte; /**< the whole byte */
|
||||
#if defined(REVERSED)
|
||||
struct
|
||||
{
|
||||
unsigned int type : 4; /**< message type nibble */
|
||||
unsigned int dup : 1; /**< DUP flag bit */
|
||||
unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */
|
||||
unsigned int retain : 1; /**< retained flag bit */
|
||||
} bits;
|
||||
#else
|
||||
struct
|
||||
{
|
||||
unsigned int retain : 1; /**< retained flag bit */
|
||||
unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */
|
||||
unsigned int dup : 1; /**< DUP flag bit */
|
||||
unsigned int type : 4; /**< message type nibble */
|
||||
} bits;
|
||||
#endif
|
||||
} MQTTHeader;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int len;
|
||||
char* data;
|
||||
} MQTTLenString;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char* cstring;
|
||||
MQTTLenString lenstring;
|
||||
} MQTTString;
|
||||
|
||||
#define MQTTString_initializer {NULL, {0, NULL}}
|
||||
|
||||
int MQTTstrlen(MQTTString mqttstring);
|
||||
|
||||
#include "MQTTConnect.h"
|
||||
#include "MQTTPublish.h"
|
||||
#include "MQTTSubscribe.h"
|
||||
#include "MQTTUnsubscribe.h"
|
||||
#include "MQTTFormat.h"
|
||||
|
||||
int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char type, unsigned char dup, unsigned short packetid);
|
||||
int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen);
|
||||
|
||||
int MQTTPacket_len(int rem_len);
|
||||
int MQTTPacket_equals(MQTTString* a, char* b);
|
||||
|
||||
int MQTTPacket_encode(unsigned char* buf, int length);
|
||||
int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value);
|
||||
int MQTTPacket_decodeBuf(unsigned char* buf, int* value);
|
||||
|
||||
int readInt(unsigned char** pptr);
|
||||
char readChar(unsigned char** pptr);
|
||||
void writeChar(unsigned char** pptr, char c);
|
||||
void writeInt(unsigned char** pptr, int anInt);
|
||||
int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata);
|
||||
void writeCString(unsigned char** pptr, const char* string);
|
||||
void writeMQTTString(unsigned char** pptr, MQTTString mqttstring);
|
||||
|
||||
DLLExport int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int));
|
||||
|
||||
typedef struct {
|
||||
int (*getfn)(void *, unsigned char*, int); /* must return -1 for error, 0 for call again, or the number of bytes read */
|
||||
void *sck; /* pointer to whatever the system may use to identify the transport */
|
||||
int multiplier;
|
||||
int rem_len;
|
||||
int len;
|
||||
char state;
|
||||
}MQTTTransport;
|
||||
|
||||
int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp);
|
||||
|
||||
#ifdef __cplusplus /* If this is a C++ compiler, use C linkage */
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* MQTTPACKET_H_ */
|
||||
38
arduino/libraries/Temboo/src/utility/MQTTPublish.h
Normal file
38
arduino/libraries/Temboo/src/utility/MQTTPublish.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MQTTPUBLISH_H_
|
||||
#define MQTTPUBLISH_H_
|
||||
|
||||
#if !defined(DLLImport)
|
||||
#define DLLImport
|
||||
#endif
|
||||
#if !defined(DLLExport)
|
||||
#define DLLExport
|
||||
#endif
|
||||
|
||||
DLLExport int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid,
|
||||
MQTTString topicName, unsigned char* payload, int payloadlen);
|
||||
|
||||
DLLExport int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName,
|
||||
unsigned char** payload, int* payloadlen, unsigned char* buf, int len);
|
||||
|
||||
DLLExport int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid);
|
||||
DLLExport int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid);
|
||||
DLLExport int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid);
|
||||
|
||||
#endif /* MQTTPUBLISH_H_ */
|
||||
168
arduino/libraries/Temboo/src/utility/MQTTSerializePublish.c
Normal file
168
arduino/libraries/Temboo/src/utility/MQTTSerializePublish.c
Normal file
@@ -0,0 +1,168 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/**
|
||||
* Determines the length of the MQTT publish packet that would be produced using the supplied parameters
|
||||
* @param qos the MQTT QoS of the publish (packetid is omitted for QoS 0)
|
||||
* @param topicName the topic name to be used in the publish
|
||||
* @param payloadlen the length of the payload to be sent
|
||||
* @return the length of buffer needed to contain the serialized version of the packet
|
||||
*/
|
||||
int MQTTSerialize_publishLength(int qos, MQTTString topicName, int payloadlen)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
len += 2 + MQTTstrlen(topicName) + payloadlen;
|
||||
if (qos > 0)
|
||||
len += 2; /* packetid */
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the supplied publish data into the supplied buffer, ready for sending
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param dup integer - the MQTT dup flag
|
||||
* @param qos integer - the MQTT QoS value
|
||||
* @param retained integer - the MQTT retained flag
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @param topicName MQTTString - the MQTT topic in the publish
|
||||
* @param payload byte buffer - the MQTT publish payload
|
||||
* @param payloadlen integer - the length of the MQTT payload
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid,
|
||||
MQTTString topicName, unsigned char* payload, int payloadlen)
|
||||
{
|
||||
unsigned char *ptr = buf;
|
||||
MQTTHeader header = {0};
|
||||
int rem_len = 0;
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (MQTTPacket_len(rem_len = MQTTSerialize_publishLength(qos, topicName, payloadlen)) > buflen)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
header.bits.type = PUBLISH;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = qos;
|
||||
header.bits.retain = retained;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
|
||||
|
||||
writeMQTTString(&ptr, topicName);
|
||||
|
||||
if (qos > 0)
|
||||
writeInt(&ptr, packetid);
|
||||
|
||||
memcpy(ptr, payload, payloadlen);
|
||||
ptr += payloadlen;
|
||||
|
||||
rc = ptr - buf;
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the ack packet into the supplied buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param type the MQTT packet type
|
||||
* @param dup the MQTT dup flag
|
||||
* @param packetid the MQTT packet identifier
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char packettype, unsigned char dup, unsigned short packetid)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
int rc = 0;
|
||||
unsigned char *ptr = buf;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (buflen < 4)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
header.bits.type = packettype;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = 0;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */
|
||||
writeInt(&ptr, packetid);
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a puback packet into the supplied buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid)
|
||||
{
|
||||
return MQTTSerialize_ack(buf, buflen, PUBACK, packetid, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a pubrel packet into the supplied buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param dup integer - the MQTT dup flag
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid)
|
||||
{
|
||||
return MQTTSerialize_ack(buf, buflen, PUBREL, packetid, dup);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a pubrel packet into the supplied buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid)
|
||||
{
|
||||
return MQTTSerialize_ack(buf, buflen, PUBCOMP, packetid, 0);
|
||||
}
|
||||
|
||||
|
||||
39
arduino/libraries/Temboo/src/utility/MQTTSubscribe.h
Normal file
39
arduino/libraries/Temboo/src/utility/MQTTSubscribe.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MQTTSUBSCRIBE_H_
|
||||
#define MQTTSUBSCRIBE_H_
|
||||
|
||||
#if !defined(DLLImport)
|
||||
#define DLLImport
|
||||
#endif
|
||||
#if !defined(DLLExport)
|
||||
#define DLLExport
|
||||
#endif
|
||||
|
||||
DLLExport int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
|
||||
int count, MQTTString topicFilters[], int requestedQoSs[]);
|
||||
|
||||
DLLExport int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid,
|
||||
int maxcount, int* count, MQTTString topicFilters[], int requestedQoSs[], unsigned char* buf, int len);
|
||||
|
||||
DLLExport int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs);
|
||||
|
||||
DLLExport int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int len);
|
||||
|
||||
|
||||
#endif /* MQTTSUBSCRIBE_H_ */
|
||||
137
arduino/libraries/Temboo/src/utility/MQTTSubscribeClient.c
Normal file
137
arduino/libraries/Temboo/src/utility/MQTTSubscribeClient.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Determines the length of the MQTT subscribe packet that would be produced using the supplied parameters
|
||||
* @param count the number of topic filter strings in topicFilters
|
||||
* @param topicFilters the array of topic filter strings to be used in the publish
|
||||
* @return the length of buffer needed to contain the serialized version of the packet
|
||||
*/
|
||||
int MQTTSerialize_subscribeLength(int count, MQTTString topicFilters[])
|
||||
{
|
||||
int i;
|
||||
int len = 2; /* packetid */
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
len += 2 + MQTTstrlen(topicFilters[i]) + 1; /* length + topic + req_qos */
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the supplied subscribe data into the supplied buffer, ready for sending
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied bufferr
|
||||
* @param dup integer - the MQTT dup flag
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @param count - number of members in the topicFilters and reqQos arrays
|
||||
* @param topicFilters - array of topic filter names
|
||||
* @param requestedQoSs - array of requested QoS
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, int count,
|
||||
MQTTString topicFilters[], int requestedQoSs[])
|
||||
{
|
||||
unsigned char *ptr = buf;
|
||||
MQTTHeader header = {0};
|
||||
int rem_len = 0;
|
||||
int rc = 0;
|
||||
int i = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (MQTTPacket_len(rem_len = MQTTSerialize_subscribeLength(count, topicFilters)) > buflen)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
header.byte = 0;
|
||||
header.bits.type = SUBSCRIBE;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = 1;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
|
||||
|
||||
writeInt(&ptr, packetid);
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
writeMQTTString(&ptr, topicFilters[i]);
|
||||
writeChar(&ptr, requestedQoSs[i]);
|
||||
}
|
||||
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into suback data
|
||||
* @param packetid returned integer - the MQTT packet identifier
|
||||
* @param maxcount - the maximum number of members allowed in the grantedQoSs array
|
||||
* @param count returned integer - number of members in the grantedQoSs array
|
||||
* @param grantedQoSs returned array of integers - the granted qualities of service
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success, 0 is failure
|
||||
*/
|
||||
int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int buflen)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = 0;
|
||||
int mylen;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != SUBACK)
|
||||
goto exit;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
if (enddata - curdata < 2)
|
||||
goto exit;
|
||||
|
||||
*packetid = readInt(&curdata);
|
||||
|
||||
*count = 0;
|
||||
while (curdata < enddata)
|
||||
{
|
||||
if (*count > maxcount)
|
||||
{
|
||||
rc = -1;
|
||||
goto exit;
|
||||
}
|
||||
grantedQoSs[(*count)++] = readChar(&curdata);
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
112
arduino/libraries/Temboo/src/utility/MQTTSubscribeServer.c
Normal file
112
arduino/libraries/Temboo/src/utility/MQTTSubscribeServer.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into subscribe data
|
||||
* @param dup integer returned - the MQTT dup flag
|
||||
* @param packetid integer returned - the MQTT packet identifier
|
||||
* @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays
|
||||
* @param count - number of members in the topicFilters and requestedQoSs arrays
|
||||
* @param topicFilters - array of topic filter names
|
||||
* @param requestedQoSs - array of requested QoS
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[],
|
||||
int requestedQoSs[], unsigned char* buf, int buflen)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = -1;
|
||||
int mylen = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != SUBSCRIBE)
|
||||
goto exit;
|
||||
*dup = header.bits.dup;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
|
||||
*packetid = readInt(&curdata);
|
||||
|
||||
*count = 0;
|
||||
while (curdata < enddata)
|
||||
{
|
||||
if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata))
|
||||
goto exit;
|
||||
if (curdata >= enddata) /* do we have enough data to read the req_qos version byte? */
|
||||
goto exit;
|
||||
requestedQoSs[*count] = readChar(&curdata);
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the supplied suback data into the supplied buffer, ready for sending
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @param count - number of members in the grantedQoSs array
|
||||
* @param grantedQoSs - array of granted QoS
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
int rc = -1;
|
||||
unsigned char *ptr = buf;
|
||||
int i;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (buflen < 2 + count)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
header.byte = 0;
|
||||
header.bits.type = SUBACK;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, 2 + count); /* write remaining length */
|
||||
|
||||
writeInt(&ptr, packetid);
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
writeChar(&ptr, grantedQoSs[i]);
|
||||
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
38
arduino/libraries/Temboo/src/utility/MQTTUnsubscribe.h
Normal file
38
arduino/libraries/Temboo/src/utility/MQTTUnsubscribe.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MQTTUNSUBSCRIBE_H_
|
||||
#define MQTTUNSUBSCRIBE_H_
|
||||
|
||||
#if !defined(DLLImport)
|
||||
#define DLLImport
|
||||
#endif
|
||||
#if !defined(DLLExport)
|
||||
#define DLLExport
|
||||
#endif
|
||||
|
||||
DLLExport int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
|
||||
int count, MQTTString topicFilters[]);
|
||||
|
||||
DLLExport int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int max_count, int* count, MQTTString topicFilters[],
|
||||
unsigned char* buf, int len);
|
||||
|
||||
DLLExport int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid);
|
||||
|
||||
DLLExport int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int len);
|
||||
|
||||
#endif /* MQTTUNSUBSCRIBE_H_ */
|
||||
106
arduino/libraries/Temboo/src/utility/MQTTUnsubscribeClient.c
Normal file
106
arduino/libraries/Temboo/src/utility/MQTTUnsubscribeClient.c
Normal file
@@ -0,0 +1,106 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Determines the length of the MQTT unsubscribe packet that would be produced using the supplied parameters
|
||||
* @param count the number of topic filter strings in topicFilters
|
||||
* @param topicFilters the array of topic filter strings to be used in the publish
|
||||
* @return the length of buffer needed to contain the serialized version of the packet
|
||||
*/
|
||||
int MQTTSerialize_unsubscribeLength(int count, MQTTString topicFilters[])
|
||||
{
|
||||
int i;
|
||||
int len = 2; /* packetid */
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
len += 2 + MQTTstrlen(topicFilters[i]); /* length + topic*/
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the supplied unsubscribe data into the supplied buffer, ready for sending
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @param dup integer - the MQTT dup flag
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @param count - number of members in the topicFilters array
|
||||
* @param topicFilters - array of topic filter names
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
|
||||
int count, MQTTString topicFilters[])
|
||||
{
|
||||
unsigned char *ptr = buf;
|
||||
MQTTHeader header = {0};
|
||||
int rem_len = 0;
|
||||
int rc = -1;
|
||||
int i = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (MQTTPacket_len(rem_len = MQTTSerialize_unsubscribeLength(count, topicFilters)) > buflen)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
header.byte = 0;
|
||||
header.bits.type = UNSUBSCRIBE;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = 1;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
|
||||
|
||||
writeInt(&ptr, packetid);
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
writeMQTTString(&ptr, topicFilters[i]);
|
||||
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into unsuback data
|
||||
* @param packetid returned integer - the MQTT packet identifier
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success, 0 is failure
|
||||
*/
|
||||
int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int buflen)
|
||||
{
|
||||
unsigned char type = 0;
|
||||
unsigned char dup = 0;
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
rc = MQTTDeserialize_ack(&type, &dup, packetid, buf, buflen);
|
||||
if (type == UNSUBACK)
|
||||
rc = 1;
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
102
arduino/libraries/Temboo/src/utility/MQTTUnsubscribeServer.c
Normal file
102
arduino/libraries/Temboo/src/utility/MQTTUnsubscribeServer.c
Normal file
@@ -0,0 +1,102 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into unsubscribe data
|
||||
* @param dup integer returned - the MQTT dup flag
|
||||
* @param packetid integer returned - the MQTT packet identifier
|
||||
* @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays
|
||||
* @param count - number of members in the topicFilters and requestedQoSs arrays
|
||||
* @param topicFilters - array of topic filter names
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[],
|
||||
unsigned char* buf, int len)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = 0;
|
||||
int mylen = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != UNSUBSCRIBE)
|
||||
goto exit;
|
||||
*dup = header.bits.dup;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
|
||||
*packetid = readInt(&curdata);
|
||||
|
||||
*count = 0;
|
||||
while (curdata < enddata)
|
||||
{
|
||||
if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata))
|
||||
goto exit;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the supplied unsuback data into the supplied buffer, ready for sending
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
int rc = 0;
|
||||
unsigned char *ptr = buf;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (buflen < 2)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
header.byte = 0;
|
||||
header.bits.type = UNSUBACK;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */
|
||||
|
||||
writeInt(&ptr, packetid);
|
||||
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
78
arduino/libraries/Temboo/src/utility/StackTrace.h
Normal file
78
arduino/libraries/Temboo/src/utility/StackTrace.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - fix for bug #434081
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef STACKTRACE_H_
|
||||
#define STACKTRACE_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#define NOSTACKTRACE 1
|
||||
|
||||
#if defined(NOSTACKTRACE)
|
||||
#define FUNC_ENTRY
|
||||
#define FUNC_ENTRY_NOLOG
|
||||
#define FUNC_ENTRY_MED
|
||||
#define FUNC_ENTRY_MAX
|
||||
#define FUNC_EXIT
|
||||
#define FUNC_EXIT_NOLOG
|
||||
#define FUNC_EXIT_MED
|
||||
#define FUNC_EXIT_MAX
|
||||
#define FUNC_EXIT_RC(x)
|
||||
#define FUNC_EXIT_MED_RC(x)
|
||||
#define FUNC_EXIT_MAX_RC(x)
|
||||
|
||||
#else
|
||||
|
||||
#if defined(WIN32)
|
||||
#define inline __inline
|
||||
#define FUNC_ENTRY StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MINIMUM)
|
||||
#define FUNC_ENTRY_NOLOG StackTrace_entry(__FUNCTION__, __LINE__, -1)
|
||||
#define FUNC_ENTRY_MED StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MEDIUM)
|
||||
#define FUNC_ENTRY_MAX StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MAXIMUM)
|
||||
#define FUNC_EXIT StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MINIMUM)
|
||||
#define FUNC_EXIT_NOLOG StackTrace_exit(__FUNCTION__, __LINE__, -1)
|
||||
#define FUNC_EXIT_MED StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MEDIUM)
|
||||
#define FUNC_EXIT_MAX StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MAXIMUM)
|
||||
#define FUNC_EXIT_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MINIMUM)
|
||||
#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MEDIUM)
|
||||
#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MAXIMUM)
|
||||
#else
|
||||
#define FUNC_ENTRY StackTrace_entry(__func__, __LINE__, TRACE_MINIMUM)
|
||||
#define FUNC_ENTRY_NOLOG StackTrace_entry(__func__, __LINE__, -1)
|
||||
#define FUNC_ENTRY_MED StackTrace_entry(__func__, __LINE__, TRACE_MEDIUM)
|
||||
#define FUNC_ENTRY_MAX StackTrace_entry(__func__, __LINE__, TRACE_MAXIMUM)
|
||||
#define FUNC_EXIT StackTrace_exit(__func__, __LINE__, NULL, TRACE_MINIMUM)
|
||||
#define FUNC_EXIT_NOLOG StackTrace_exit(__func__, __LINE__, NULL, -1)
|
||||
#define FUNC_EXIT_MED StackTrace_exit(__func__, __LINE__, NULL, TRACE_MEDIUM)
|
||||
#define FUNC_EXIT_MAX StackTrace_exit(__func__, __LINE__, NULL, TRACE_MAXIMUM)
|
||||
#define FUNC_EXIT_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MINIMUM)
|
||||
#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MEDIUM)
|
||||
#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MAXIMUM)
|
||||
|
||||
void StackTrace_entry(const char* name, int line, int trace);
|
||||
void StackTrace_exit(const char* name, int line, void* return_value, int trace);
|
||||
|
||||
void StackTrace_printStack(FILE* dest);
|
||||
char* StackTrace_get(unsigned long);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* STACKTRACE_H_ */
|
||||
101
arduino/libraries/Temboo/src/utility/TembooCoAPIPStack.h
Normal file
101
arduino/libraries/Temboo/src/utility/TembooCoAPIPStack.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOCOAPIPSTACK_H_
|
||||
#define TEMBOOCOAPIPSTACK_H_
|
||||
|
||||
#include <string.h>
|
||||
#include <Udp.h>
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
class TembooCoAPIPStack
|
||||
{
|
||||
public:
|
||||
enum Error {
|
||||
SUCCESS = 0,
|
||||
ERROR_RESOLVING,
|
||||
ERROR_WRITING,
|
||||
ERROR_SENDING,
|
||||
ERROR_RECEIVING
|
||||
|
||||
};
|
||||
|
||||
|
||||
TembooCoAPIPStack(UDP& udp) : m_udp(udp) {}
|
||||
|
||||
|
||||
int sendDatagram(IPAddress address, uint16_t port, uint8_t* data, size_t len) {
|
||||
if (0 == m_udp.beginPacket(address, port)) {
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("UDP begin");
|
||||
return ERROR_RESOLVING;
|
||||
}
|
||||
|
||||
uint16_t c = m_udp.write(data, len);
|
||||
if (len != c) {
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("UDP write");
|
||||
Serial.println(len);
|
||||
Serial.println(c);
|
||||
Serial.println((char*)data);
|
||||
|
||||
return ERROR_WRITING;
|
||||
}
|
||||
|
||||
if (0 == m_udp.endPacket()) {
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("UDP send");
|
||||
return ERROR_SENDING;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int recvDatagram(uint8_t* buffer, size_t maxLen, int32_t& count) {
|
||||
memset(buffer, 0, maxLen);
|
||||
count = 0;
|
||||
if (m_udp.parsePacket() > 0) {
|
||||
count = m_udp.read(buffer, maxLen);
|
||||
if (count < 0) {
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("UDP read");
|
||||
return ERROR_RECEIVING;
|
||||
}
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
IPAddress getRemoteAddress() {
|
||||
return m_udp.remoteIP();
|
||||
}
|
||||
|
||||
uint16_t getRemotePort() {
|
||||
return m_udp.remotePort();
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
UDP& m_udp;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
203
arduino/libraries/Temboo/src/utility/TembooCoAPSession.cpp
Normal file
203
arduino/libraries/Temboo/src/utility/TembooCoAPSession.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "TembooCoAPSession.h"
|
||||
#include "tmbhmac.h"
|
||||
#include "DataFormatter.h"
|
||||
#include "TembooTags.h"
|
||||
|
||||
#define QSEND(x) if(!qsend((x))) { goto SendFailed;}
|
||||
|
||||
const size_t MAX_HOST_LEN = 64;
|
||||
const size_t MAX_URL_PATH_LEN = 100;
|
||||
|
||||
const char EOL[] = "\r\n";
|
||||
const char POST[] = "POST ";
|
||||
const char POSTAMBLE[] = " HTTP/1.0"; // Prevent host from using chunked encoding in response.
|
||||
const char HEADER_HOST[] = "Host: ";
|
||||
const char HEADER_ACCEPT[] = "Accept: application/xml";
|
||||
const char HEADER_ORG[] = "x-temboo-domain: /";
|
||||
const char HEADER_DOM[] = "/master";
|
||||
const char HEADER_CONTENT_LENGTH[] = "Content-Length: ";
|
||||
const char HEADER_TIME[] = "x-temboo-time: ";
|
||||
const char BASE_CHOREO_URI[] = "/arcturus-web/api-1.0/ar";
|
||||
const char HEADER_AUTH[] = "x-temboo-authentication: ";
|
||||
const char HEADER_CONTENT_TYPE[] = "Content-Type: text/plain";
|
||||
const char TEMBOO_DOMAIN[] = ".temboolive.com";
|
||||
const char SDK_ID[] = "?source_id=CoAPGateway_1";
|
||||
|
||||
|
||||
unsigned long TembooCoAPSession::s_timeOffset = 0;
|
||||
|
||||
TembooCoAPSession::TembooCoAPSession(TembooCoAPClient& client) : m_client(client) {
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPSession::setTime(unsigned long currentTime) {
|
||||
s_timeOffset = currentTime - (millis()/1000);
|
||||
}
|
||||
|
||||
|
||||
unsigned long TembooCoAPSession::getTime() {
|
||||
return s_timeOffset + (millis()/1000);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int TembooCoAPSession::executeChoreo(
|
||||
uint16_t requestId,
|
||||
const char* accountName,
|
||||
const char* appKeyName,
|
||||
const char* appKeyValue,
|
||||
const char* path,
|
||||
const ChoreoInputSet& inputSet,
|
||||
const ChoreoInputExpressionSet& expressionSet,
|
||||
const ChoreoSensorInputSet& sensorSet,
|
||||
const ChoreoOutputSet& outputSet,
|
||||
const ChoreoPreset& preset,
|
||||
const ChoreoDevice& deviceType,
|
||||
const ChoreoDevice& deviceName){
|
||||
|
||||
DataFormatter fmt(&inputSet, &expressionSet, &sensorSet,&outputSet, &preset, &deviceType, &deviceName);
|
||||
char auth[HMAC_HEX_SIZE_BYTES + 1];
|
||||
char timeStr[11];
|
||||
char requestIdStr[5];
|
||||
uint16toa(requestId, requestIdStr);
|
||||
|
||||
// We use the current time-of-day as salt on the app key.
|
||||
// We keep track of time-of-day by getting the current time
|
||||
// from the server and applying an offset (the length of time
|
||||
// we've been running.)
|
||||
uint32toa((uint32_t)TembooCoAPSession::getTime(), timeStr);
|
||||
|
||||
getAuth(fmt, appKeyValue, timeStr, auth);
|
||||
|
||||
m_client.clearData();
|
||||
|
||||
QSEND(TAG_REQUEST_ID);
|
||||
QSEND(requestIdStr);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
QSEND(TAG_ACCOUNT_NAME);
|
||||
QSEND(accountName);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
QSEND(TAG_APP_KEY_NAME);
|
||||
QSEND(appKeyName);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
QSEND(TAG_TIME);
|
||||
QSEND(timeStr);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
QSEND(TAG_AUTH);
|
||||
QSEND(auth);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
QSEND(TAG_CHOREO_ID);
|
||||
QSEND(path);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
QSEND(TAG_DATA);
|
||||
fmt.reset();
|
||||
while(fmt.hasNext()) {
|
||||
QSEND(fmt.next());
|
||||
}
|
||||
|
||||
QSEND(TAG_END_REQUEST);
|
||||
|
||||
return m_client.sendChoreoRequest();
|
||||
|
||||
SendFailed:
|
||||
TEMBOO_TRACELN("FAIL");
|
||||
return FAILURE;
|
||||
|
||||
}
|
||||
|
||||
uint16_t TembooCoAPSession::getAuth(DataFormatter& fmt, const char* appKeyValue, const char* salt, char* result) const {
|
||||
|
||||
// We need the length of the data for other things, and
|
||||
// this method is a convenient place to calculate it.
|
||||
uint16_t len = 0;
|
||||
|
||||
HMAC hmac;
|
||||
|
||||
//combine the salt and the key and give it to the HMAC calculator
|
||||
size_t keyLength = strlen(appKeyValue) + strlen(salt);
|
||||
char key[keyLength + 1];
|
||||
strcpy(key, salt);
|
||||
strcat(key, appKeyValue);
|
||||
hmac.init((uint8_t*)key, keyLength);
|
||||
|
||||
// process the data a block at a time.
|
||||
uint8_t buffer[HMAC_BLOCK_SIZE_BYTES];
|
||||
int blockCount = 0;
|
||||
fmt.reset();
|
||||
while(fmt.hasNext()) {
|
||||
uint8_t c = fmt.next();
|
||||
len++;
|
||||
buffer[blockCount++] = c;
|
||||
if (blockCount == HMAC_BLOCK_SIZE_BYTES) {
|
||||
hmac.process(buffer, blockCount);
|
||||
blockCount = 0;
|
||||
}
|
||||
}
|
||||
hmac.process(buffer, blockCount);
|
||||
|
||||
// Finalize the HMAC calculation and store the (ASCII HEX) value in *result.
|
||||
hmac.finishHex(result);
|
||||
|
||||
// Return the number of characters processed.
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
bool TembooCoAPSession::qsend(const char* s) {
|
||||
char c = *s++;
|
||||
bool rc = true;
|
||||
while(c != '\0' && rc) {
|
||||
rc = qsend(c);
|
||||
c = *s++;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
bool TembooCoAPSession::qsend(char c) {
|
||||
// Never send a nul character.
|
||||
if ('\0' != c) {
|
||||
if (TembooCoAPClient::NO_ERROR == m_client.write(c)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
115
arduino/libraries/Temboo/src/utility/TembooCoAPSession.h
Normal file
115
arduino/libraries/Temboo/src/utility/TembooCoAPSession.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOCOAPSESSIONCLASS_H_
|
||||
#define TEMBOOCOAPSESSIONCLASS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <Arduino.h>
|
||||
#include "TembooCoAPEdgeDevice.h"
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
#ifndef TEMBOO_SEND_QUEUE_SIZE
|
||||
#define TEMBOO_SEND_QUEUE_SIZE (1000)
|
||||
#endif
|
||||
|
||||
class ChoreoInputSet;
|
||||
class ChoreoOutputSet;
|
||||
class ChoreoPreset;
|
||||
class DataFormatter;
|
||||
|
||||
class TembooCoAPSession {
|
||||
public:
|
||||
|
||||
//TembooSession constructor
|
||||
//client: REQUIRED TembooCoAPClient client object.
|
||||
TembooCoAPSession(TembooCoAPClient& client);
|
||||
|
||||
//executeChoreo sends a choreo execution request to the Temboo system.
|
||||
// Does not wait for a response (that's a job for whoever owns the Client.)
|
||||
//accountName: the name of the user's account at Temboo.
|
||||
//appKeyName: the name of an application key in the user's account to use
|
||||
// for this execution (analogous to a user name).
|
||||
//appKeyValue: the value of the application key named in appKeyName.
|
||||
// Used to authenticate the user (analogous to a password)
|
||||
//path: The full path to the choreo to be executed (relative to the root of the
|
||||
// user's account.)
|
||||
//inputSet: the set of inputs needed by the choreo.
|
||||
// May be an empty ChoreoInputSet.
|
||||
//outputSet: the set of output filters to be applied to the choreo results.
|
||||
// May be an empty ChoreoOutputSet
|
||||
//preset: the ChoreoPreset to be used with the choreo execution.
|
||||
// May be an empty ChoreoPreset.
|
||||
int executeChoreo(
|
||||
uint16_t requestId,
|
||||
const char* accountName,
|
||||
const char* appKeyName,
|
||||
const char* appKeyValue,
|
||||
const char* path,
|
||||
const ChoreoInputSet& inputSet,
|
||||
const ChoreoInputExpressionSet& expressionSet,
|
||||
const ChoreoSensorInputSet& sensorSet,
|
||||
const ChoreoOutputSet& outputSet,
|
||||
const ChoreoPreset& preset,
|
||||
const ChoreoDevice& deviceType,
|
||||
const ChoreoDevice& deviceName);
|
||||
|
||||
// setTime sets the current time in Unix timestamp format. Needed for execution request authentication.
|
||||
// NOTE: This method is usually called by TembooChoreo.run() with the current time returned by
|
||||
// an error response from the Temboo system, thus automatically setting the time. However, it
|
||||
// MAY be called from user code if the particular board has a way of determining the current
|
||||
// time in the proper format.
|
||||
// currentTime: the number of seconds since 1970-01-01 00:00:00 UTC.
|
||||
static void setTime(unsigned long currentTime);
|
||||
|
||||
//getTime returns the current time in Unix timestamp format (seconds since 1970-01-01 00:00:00 UTC).
|
||||
// Only valid after setTime has been called.
|
||||
static unsigned long getTime();
|
||||
|
||||
enum Error {
|
||||
SUCCESS = 0,
|
||||
FAILURE
|
||||
};
|
||||
|
||||
private:
|
||||
TembooCoAPClient& m_client;
|
||||
static unsigned long s_timeOffset;
|
||||
|
||||
// calculate the authentication code value of the formatted request body
|
||||
// using the salted application key value as the key.
|
||||
// Returns the number of characters processed (i.e. the length of the request body)
|
||||
uint16_t getAuth(DataFormatter& fmt, const char* appKeyValue, const char* salt, char* hexAuth) const;
|
||||
|
||||
|
||||
// queue an entire nul-terminated char array
|
||||
// from RAM one byte at a time.
|
||||
// returns true on success, false on failure
|
||||
bool qsend(const char*);
|
||||
|
||||
// queue a single character to be sent.
|
||||
// returns true on success, false on failure
|
||||
bool qsend(char);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user