Getting Started

If you haven’t installed the SDK yet, please head over to the QuickStart guide to install the SDK in the Arduino IDE. Note that this SDK requires the Arduino Yún and the Arduino IDE v1.6.0+.

The Parse platform provides a complete backend solution for your hardware device. Our goal is to totally eliminate the need for writing server code or maintaining servers.

Note that the Arduino SDK only contains a subset of Parse features found in mobile and desktop. This allows the SDK to be smaller in size and more performant, making it suitable for constrained embedded environments. The Arduino SDK is fully open source, and anyone can contribute to make it better, or make their own changes if necessary. Check out the GitHub repository for more information.

Initialization

In order for Parse to know which app is associated with the Arduino device, simply specify the application ID and client key in your setup function:

void setup() {
	Parse.begin("${APPLICATION_ID}", "${CLIENT_KEY}");
	// ...
}

After this, all calls to the Parse Server will use the specified app.

Want to contribute to this doc? Edit this section.

Objects

The Parse Object

Storing data on Parse is built around the Parse Object. Each Parse Object contains key-value pairs of JSON-compatible data. This data is schemaless, which means that you don’t need to specify ahead of time what keys exist on each object. You simply set whatever key-value pairs you want, and our backend will store it.

For example, let’s say you’re tracking data for your smart toaster. A single Parse Object could contain:

temperature: 175.0, leverDown: true

Keys must be alphanumeric strings. Values can be strings, numbers, booleans, or even arrays and dictionaries - anything that can be JSON-encoded.

Each object has a class name that you can use to distinguish different sorts of data. For example, we store the temperature data in a class called Temperature. We recommend that you NameYourClassesLikeThis and nameYourKeysLikeThis, just to keep your code looking pretty.

Saving Objects

Let’s say you want to save the Temperature described above to the Parse Cloud. You would do the following:

ParseObjectCreate create;
create.setClassName("Temperature");
create.add("temperature", 175.0);
create.add("leverDown", true);
ParseResponse response = create.send();
if (!response.getErrorCode()) {
	// The object has been saved
} else {
	// There was a problem, check response.
	getErrorCode();
}
response.close(); // Free the resource

After this code runs, you will probably be wondering if anything really happened. To make sure the data was saved, you can look at the Data Browser in your app on Parse. You should see something like this:

	objectId: "xWMyZ4YEGZ", temperature: 175.0, leverDown: true, createdAt: "2011-06-10T18:33:42Z", updatedAt: "2011-06-10T18:33:42Z"

There are two things to note here. You didn’t have to configure or set up a new Class called Temperature before running this code. Your Parse app lazily creates this Class for you when it first encounters it.

There are also a few fields you don’t need to specify that are provided as a convenience. objectId is a unique identifier for each saved object. createdAt andupdatedAt represent the time that each object was created and last modified in the Parse Cloud. Each of these fields is filled in by Parse, so they don’t exist on a Parse Object until a save operation has completed.

Retrieving Objects

Saving data to the cloud is fun, but it’s even more fun to get that data out again. If you have the objectId, you can retrieve the object using a query:

ParseObjectGet get;
get.setClassName("Temperature");
get.setObjectId("xWMyZ4YEGZ");
ParseResponse response = get.send();
double temp = response.getDouble("temperature");
Serial.println(temp);
response.close(); // Free the resource

Updating Objects

Updating an object is very similar to creating one. Assuming you have saved the object and have the objectId, can do the following:

ParseObjectUpdate update;
update.setClassName("Temperature");
update.setObjectId("xWMyZ4YEGZ");
update.add("temperature", 100);
update.send();

Deleting Objects

To delete an object from the cloud:

ParseObjectDelete del;
del.setClassName("Temperature");
del.setObjectId("xWMyZ4YEGZ");
del.send();

Data Types

So far we’ve used values with type string, double, and bool. The Parse Arduino SDK also supports GeoPoints (latitude and longitude). In addition, you can set values on objects via JSON and be able to represent Arrays, Objects, Dates, and more. Read more about representing these types as JSON in the REST API guide.

Overall, the following types are allowed for each field in your object:

  • String => string
  • Number => integer, double
  • Boolean => bool
  • Array => JSON Array
  • Object => JSON Object
  • Date => JSON Date
  • File => ParseFile
  • Pointer => other ParseObject
  • Relation => ParseRelation

The type JSON Object simply denotes that each value can be composed of nested objects that are JSON-encodable. Keys including the characters $ or ., along with the key __type key, are reserved for the framework to handle additional types, so don’t use those yourself.

Some examples:

ParseObjectCreate create;
create.setClassName("TestObject");
create.add("number", 42.0);
create.add("foo", "bar");
create.addGeoPoint("location", 40.0, -30.0);
create.addJSONValue("dateField", "{ \"__type\": \"Date\", \"iso\": \"2011-08-21T18:02:52.249Z\" }"); create.addJSONValue("arrayField", "[ 30, \"string\" ]");
create.addJSONValue("objectField", "{ \"number\": 30, \"string\": \"baz\" }");
create.addJSONValue("emptyField", "null");
create.send();

We do not recommend storing large pieces of binary data like images or documents in a ParseObject. ParseObjects should not exceed 128 kilobytes in size. We recommend you use ParseFiles to store images, documents, and other types of files. You can do so by instantiating a ParseFile object and setting it on a field. See the Files section in the REST documentation for more details.

For more information about how Parse handles data, check out our documentation on Data.

Want to contribute to this doc? Edit this section.

Queries

We’ve already seen how a ParseObjectGet with an objectId can retrieve a single object from Parse. There are many other ways to retrieve data with ParseQuery – you can retrieve many objects at once, put conditions on the objects you wish to retrieve, and more.

Basic Queries

The general pattern is to create a ParseQuery, put conditions on it, and then retrieve objects from the response. For example, to retrieve temperature data at a particular temperature, use thewhereEqualToInt function to constrain the value for a key.

ParseQuery query;
query.setClassName("Temperature");
query.whereEqualTo("temperature", 100.0);
ParseResponse response = query.send();
int countOfResults = response.count();
Serial.println(countOfResults);
while(response.nextObject()) {
	Serial.println(response.getJSONBody());
	Serial.println(response.getDouble("temperature"));
	Serial.println(response.getString("createdAt"));
}
response.close(); // Free the resource

Query Constraints

Your query should have at least one constraint. There are several ways to put constraints on the objects found by a query. You can filter out objects with a particular key-value pair with:

query.whereNotEqualTo("toaster", "foo");

You can give multiple constraints, and objects will only be in the results if they match all of the constraints. In other words, it’s like an AND of constraints.

query.whereEqualTo("leverDown", true);
query.whereEqualTo("temperature", 100.0);

You can limit the number of results by setting a limit. By default, results are limited to 100, but anything from 1 to 1000 is a valid limit:

query.setLimit(10);

You can skip the first results by setting skip. This can be useful for pagination:

query.setSkip(10);

For sortable types like numbers and strings, you can control the order in which results are returned:

// Sorts the results in ascending order by the temperature field
query.orderBy("temperature");
// Sorts the results in descending order by the temperature field
query.orderBy("-temperature");

You can sort by multiple keys as follows:

query.orderBy("temperature,name");

For sortable types, you can also use comparisons in queries:

// Restricts to temperatures < 50
query.whereLessThan("temperature", 50.0);
// Restricts to temperatures > 50 query.
whereGreaterThan("temperature", 50.0);
// Restricts to temperatures >= 50 query.
whereGreaterThanOrEqualTo("temperature", 50.0);

You can restrict the fields returned by calling setKeys with a list of keys as a string. To retrieve objects that contain only the temperature field (and also special built-in fields such asobjectId,createdAt, andupdatedAt):

query.setKeys("temperature");

This is useful if the object has fields which are not required by the device. Since the Arduino is a constrained environment, we recommend using a combination of setKeys andsetLimit to reduce processing and memory overhead.

Want to contribute to this doc? Edit this section.

Requests

Because the Arduino SDK was designed to minimize memory footprint, it doesn’t provide direct functions for all the Parse features that are present in the mobile SDKs. However, it does have the capability to call the REST API, which offers the full range of functionality.

For example, you could sign up a user from Arduino through a REST call:

ParseResponse response = Parse.sendRequest("POST", "/1/users", "{\"username\":\"cooldude6\",\"password\":\"p_n7!-e8\"}", "");

In this case, the response will contain the objectId of the created user, assuming it was created successfully.

Head on over to the REST API documentation to discover what’s possible. For each code sample, you can switch it to cURL to see what the endpoint and payload would look like.

Want to contribute to this doc? Edit this section.

Users and Sessions

At the core of many apps, there is a notion of user accounts that lets users access their information in a secure manner. In our other SDKs, we provide a specialized user class that automatically handles much of the functionality required for user account management. Users are a special class of Parse Objects and has all the same features, such as flexible schema, automatic persistence, and a key value interface.

The Arduino SDK does not provide methods to directly sign in as a user. If you want to have the Arduino device act on behalf of a user, you will need to create a Session through a companion app or another Parse SDK and pass a Restricted Session to the device. You can read more about users in our REST API or one of our other SDK guides.

Setting a Session

Once you have created a Restricted Session via the companion app or through the REST API, you can send the Session Token to the Arduino using push notifications, BLE, or some other appropriate method. After that, you can set it:

Parse.setSessionToken("r:olqZkbv8fefVFNjWegyIXIggd");

From then on, the device will act on behalf of the user.

Security

Parse uses ACLs to make sure objects can be accessed by users who are authorized with access. When creating objects, you can set the ACL field of the object to restrict access to a set of users. Read about this and more in the REST API documentation.

Want to contribute to this doc? Edit this section.

Push Notifications

Using Push Notifications, you’ll be able to send realtime notifications to your Arduino. This can be triggered by changes in your data, custom code in Cloud Code, a companion mobile app, and much more.

Installations

Every Parse application installed on a device registered for push notifications has an associated Installation object. The Installation object is where you store all the data needed to target push notifications. For example, you could send a connected thermostat a push to change the desired temperature.

There are two ways to create an Installation for the Arduino. You can generate an installation ID (random lowercase UUID) elsewhere (e.g. phone), send that to your arduino during initial provisioning, then set the installation ID on the arduino:

// In this example, we associate this device with a pre-generated installation ID
Parse.setInstallationId("ab946c14-757a-4448-8b77-69704b01bb7b");

The installation ID is a unique identifier for the device, so you should make sure to assign different installation IDs to different devices (i.e. your UUID generator has enough randomness). After you do the above, the arduino will automatically create an Installation object with this installation ID.

If you do not pass in an installation ID, theParseClient will automatically generate an installation ID for you, and create an Installation object with it upon the first request sent to Parse.

You can retrieve your installation ID with thegetInstallationId function:

String installationId = Parse.getInstallationId();

The installation ID is persisted across reboots.

The Installation class has several special fields that help you manage and target devices. The relevant ones for Arduino are:

  • channels: An array of the channels to which a device is currently subscribed.
  • deviceType: The type of device, “ios”, “android”, “winrt”, “winphone”, “dotnet”, or “embedded” (readonly).
  • installationId: Universally Unique Identifier (UUID) for the device used by Parse. It must be unique across all of an app’s installations.(readonly).
  • appName: The display name of the client application to which this installation belongs.
  • appVersion: The version string of the client application to which this installation belongs.
  • parseVersion: The version of the Parse SDK which this installation uses.

Subscribing to Pushes

To subscribe to push notifications, make the following call in yoursetup function:

Parse.startPushService();

Then, in your loop function:

if (Parse.pushAvailable()) {
	ParsePush push = Parse.nextPush();
	// Print whole JSON body
	String message = push.getJSONBody();
	Serial.print("New push message size: ");
	Serial.println(message.length());
	Serial.print("New push message content: ");
	Serial.println(message);
	// Do something with the push
	// IMPORTANT, close your push message
	push.close();
}

Sending Pushes

There are many ways to send a push notification. It’s possible to send from the Arduino SDK via a call to the REST API, but you’ll most likely be sending from another environment. Read here for more information on how to send pushes.

Want to contribute to this doc? Edit this section.

Analytics

Some examples:

ParseTrackEvent trackEvent;
trackEvent.setEventName("ButtonPress");
trackEvent.send();
Want to contribute to this doc? Edit this section.

Cloud Functions

Cloud Functions allow you to run custom app logic in the Parse Cloud. This is especially useful for running complex app logic in the cloud so that you can reduce the memory footprint of your code on the IoT device. In a Cloud Function, you can query/save Parse data, send push notifications, and log analytics events.

You write your Cloud Code in JavaScript using the Parse JavaScript SDK. We provide a command-line tool to help you deploy your Cloud Code. See our Cloud Code guide for details.

For example, you define a Cloud Function as below.

Parse.Cloud.define("hello", function(request, response) {
	response.success(request.body);
});

Then you can invoke this Cloud Function from your device:

ParseCloudFunction cloudFunction;
cloudFunction.setFunctionName("hello");
cloudFunction.add("value", "echo from hello");
ParseResponse response = cloudFunction.send();
Want to contribute to this doc? Edit this section.

Sample App

We prepared a sample app that demonstrates how to provision connected devices using a companion phone app such that connected devices can securely access user-specific data on the Parse Cloud. This sample app also demonstrates how to send push notifications between the phone app and connected devices.

Want to contribute to this doc? Edit this section.

Error Codes

The following is a list of all the error codes that can be returned by the Parse API. You may also refer to RFC2616 for a list of http error codes. Make sure to check the error message for more details.

API Issues

Name Code Description
UserInvalidLoginParams 101 Invalid login parameters. Check error message for more details.
ObjectNotFound 101 The specified object or session doesn’t exist or could not be found. Can also indicate that you do not have the necessary permissions to read or write this object. Check error message for more details.
InvalidQuery 102 There is a problem with the parameters used to construct this query. This could be an invalid field name or an invalid field type for a specific constraint. Check error message for more details.
InvalidClassName 103 Missing or invalid classname. Classnames are case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the only valid characters.
MissingObjectId 104 An unspecified object id.
InvalidFieldName 105 An invalid field name. Keys are case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the only valid characters. Some field names may be reserved. Check error message for more details.
InvalidPointer 106 A malformed pointer was used. You would typically only see this if you have modified a client SDK.
InvalidJSON 107 Badly formed JSON was received upstream. This either indicates you have done something unusual with modifying how things encode to JSON, or the network is failing badly. Can also indicate an invalid utf-8 string or use of multiple form encoded values. Check error message for more details.
CommandUnavailable 108 The feature you tried to access is only available internally for testing purposes.
NotInitialized 109 You must call Parse.initialize before using the Parse library. Check the Quick Start guide for your platform.
ObjectTooLarge 116 The object is too large. Parse Objectss have a max size of 128 kilobytes.
ExceededConfigParamsError 116 You have reached the limit of 100 config parameters.
InvalidLimitError 117 An invalid value was set for the limit. Check error message for more details.
InvalidSkipError 118 An invalid value was set for skip. Check error message for more details.
OperationForbidden 119 The operation isn’t allowed for clients due to class-level permissions. Check error message for more details.
CacheMiss 120 The result was not found in the cache.
InvalidNestedKey 121 An invalid key was used in a nested JSONObject. Check error message for more details.
InvalidACL 123 An invalid ACL was provided.
InvalidEmailAddress 125 The email address was invalid.
DuplicateValue 137 Unique field was given a value that is already taken.
InvalidRoleName 139 Role’s name is invalid.
ReservedValue 139 Field value is reserved.
ExceededCollectionQuota 140 You have reached the quota on the number of classes in your app. Please delete some classes if you need to add a new class.
ScriptFailed 141 Cloud Code script failed. Usually points to a JavaScript error. Check error message for more details.
FunctionNotFound 141 Cloud function not found. Check that the specified Cloud function is present in your Cloud Code script and has been deployed.
JobNotFound 141 Background job not found. Check that the specified job is present in your Cloud Code script and has been deployed.
SuccessErrorNotCalled 141 success/error was not called. A cloud function will return once response.success() or response.error() is called. A background job will similarly finish execution once status.success() or status.error() is called. If a function or job never reaches either of the success/error methods, this error will be returned. This may happen when a function does not handle an error response correctly, preventing code execution from reaching the success() method call.
MultupleSuccessErrorCalls 141 Can’t call success/error multiple times. A cloud function will return once response.success() or response.error() is called. A background job will similarly finish execution once status.success() or status.error() is called. If a function or job calls success() and/or error() more than once in a single execution path, this error will be returned.
ValidationFailed 142 Cloud Code validation failed.
WebhookError 143 Webhook error.
InvalidImageData 150 Invalid image data.
UnsavedFileError 151 An unsaved file.
InvalidPushTimeError 152 An invalid push time was specified.
HostingError 158 Hosting error.
InvalidEventName 160 The provided analytics event name is invalid.
ClassNotEmpty 255 Class is not empty and cannot be dropped.
AppNameInvalid 256 App name is invalid.
MissingAPIKeyError 902 The request is missing an API key.
InvalidAPIKeyError 903 The request is using an invalid API key.
Name Code Description
IncorrectType 111 A field was set to an inconsistent type. Check error message for more details.
InvalidChannelName 112 Invalid channel name. A channel name is either an empty string (the broadcast channel) or contains only a-zA-Z0-9_ characters and starts with a letter.
InvalidSubscriptionType 113 Bad subscription type. Check error message for more details.
InvalidDeviceToken 114 The provided device token is invalid.
PushMisconfigured 115 Push is misconfigured in your app. Check error message for more details.
PushWhereAndChannels 115 Can’t set channels for a query-targeted push. You can fix this by moving the channels into your push query constraints.
PushWhereAndType 115 Can’t set device type for a query-targeted push. You can fix this by incorporating the device type constraints into your push query.
PushMissingData 115 Push is missing a ‘data’ field.
PushMissingChannels 115 Non-query push is missing a ‘channels’ field. Fix by passing a ‘channels’ or ‘query’ field.
ClientPushDisabled 115 Client-initiated push is not enabled. Check your Parse app’s push notification settings.
RestPushDisabled 115 REST-initiated push is not enabled. Check your Parse app’s push notification settings.
ClientPushWithURI 115 Client-initiated push cannot use the “uri” option.
PushQueryOrPayloadTooLarge 115 Your push query or data payload is too large. Check error message for more details.
InvalidExpirationError 138 Invalid expiration value.
MissingPushIdError 156 A push id is missing. Deprecated.
MissingDeviceTypeError 157 The device type field is missing. Deprecated.
Name Code Description
InvalidFileName 122 An invalid filename was used for Parse File. A valid file name contains only a-zA-Z0-9_. characters and is between 1 and 128 characters.
MissingContentType 126 Missing content type.
MissingContentLength 127 Missing content length.
InvalidContentLength 128 Invalid content length.
FileTooLarge 129 File size exceeds maximum allowed.
FileSaveError 130 Error saving a file.
FileDeleteError 131 File could not be deleted.
Name Code Description
InvalidInstallationIdError 132 Invalid installation id.
InvalidDeviceTypeError 133 Invalid device type.
InvalidChannelsArrayError 134 Invalid channels array value.
MissingRequiredFieldError 135 Required field is missing.
ChangedImmutableFieldError 136 An immutable field was changed.
Name Code Description
ReceiptMissing 143 Product purchase receipt is missing.
InvalidPurchaseReceipt 144 Product purchase receipt is invalid.
PaymentDisabled 145 Payment is disabled on this device.
InvalidProductIdentifier 146 The product identifier is invalid.
ProductNotFoundInAppStore 147 The product is not found in the App Store.
InvalidServerResponse 148 The Apple server response is not valid.
ProductDownloadFilesystemError 149 The product fails to download due to file system error.
Name Code Description
UsernameMissing 200 The username is missing or empty.
PasswordMissing 201 The password is missing or empty.
UsernameTaken 202 The username has already been taken.
UserEmailTaken 203 Email has already been used.
UserEmailMissing 204 The email is missing, and must be specified.
UserWithEmailNotFound 205 A user with the specified email was not found.
SessionMissing 206 A user object without a valid session could not be altered.
MustCreateUserThroughSignup 207 A user can only be created through signup.
AccountAlreadyLinked 208 An account being linked is already linked to another user.
InvalidSessionToken 209 The device’s session token is no longer valid. The application should ask the user to log in again.

Linked services errors

Name Code Description
LinkedIdMissing 250 A user cannot be linked to an account because that account’s id could not be found.
InvalidLinkedSession 251 A user with a linked (e.g. Facebook or Twitter) account has an invalid session. Check error message for more details.
InvalidGeneralAuthData 251 Invalid auth data value used.
BadAnonymousID 251 Anonymous id is not a valid lowercase UUID.
FacebookBadToken 251 The supplied Facebook session token is expired or invalid.
FacebookBadID 251 A user with a linked Facebook account has an invalid session.
FacebookWrongAppID 251 Unacceptable Facebook application id.
TwitterVerificationFailed 251 Twitter credential verification failed.
TwitterWrongID 251 Submitted Twitter id does not match the id associated with the submitted access token.
TwitterWrongScreenName 251 Submitted Twitter handle does not match the handle associated with the submitted access token.
TwitterConnectFailure 251 Twitter credentials could not be verified due to problems accessing the Twitter API.
UnsupportedService 252 A service being linked (e.g. Facebook or Twitter) is unsupported. Check error message for more details.
UsernameSigninDisabled 252 Authentication by username and password is not supported for this application. Check your Parse app’s authentication settings.
AnonymousSigninDisabled 252 Anonymous users are not supported for this application. Check your Parse app’s authentication settings.
FacebookSigninDisabled 252 Authentication by Facebook is not supported for this application. Check your Parse app’s authentication settings.
TwitterSigninDisabled 252 Authentication by Twitter is not supported for this application. Check your Parse app’s authentication settings.
InvalidAuthDataError 253 An invalid authData value was passed. Check error message for more details.
LinkingNotSupportedError 999 Linking to an external account not supported yet with signup_or_login. Use update instead.

Client-only errors

Name Code Description
ConnectionFailed 100 The connection to the Parse servers failed.
AggregateError 600 There were multiple errors. Aggregate errors have an “errors” property, which is an array of error objects with more detail about each error that occurred.
FileReadError 601 Unable to read input for a Parse File on the client.
XDomainRequest 602 A real error code is unavailable because we had to use an XDomainRequest object to allow CORS requests in Internet Explorer, which strips the body from HTTP responses that have a non-2XX status code.

Operational issues

Name Code Description
RequestTimeout 124 The request was slow and timed out. Typically this indicates that the request is too expensive to run. You may see this when a Cloud function did not finish before timing out, or when a Parse.Cloud.httpRequest connection times out.
InefficientQueryError 154 An inefficient query was rejected by the server. Refer to the Performance Guide and slow query log.
RequestLimitExceeded 155 This application has exceeded its request limit (legacy Parse.com apps only).
TemporaryRejectionError 159 An application’s requests are temporary rejected by the server (legacy Parse.com apps only).
DatabaseNotMigratedError 428 You should migrate your database as soon as possible (legacy Parse.com apps only).

Other issues

Name Code Description
OtherCause -1 An unknown error or an error unrelated to Parse occurred.
InternalServerError 1 Internal server error. No information available.
ServiceUnavailable 2 The service is currently unavailable.
ClientDisconnected 4 Connection failure.
Want to contribute to this doc? Edit this section.