diff --git a/firmware.md b/firmware.md index c506c14585..809909e902 100644 --- a/firmware.md +++ b/firmware.md @@ -2,7 +2,7 @@ title: Firmware template: reference.hbs columns: three -devices: [photon,electron,core] +devices: [photon,electron,core,raspberry-pi] order: 1 --- @@ -11,6 +11,39 @@ Particle Device Firmware ## Cloud Functions +{{#if electron}} +### Optimizing Cellular Data Use with Cloud connectivity on the Electron + +_Since 0.6.0_ + +When the device first connects to the cloud, it establishes a secure channel +and informs the cloud of the registered functions, variables and subscriptions. This uses 4400 bytes of data, plus additional data for each function, variable and subscription. + +Subsequent reconnections to the cloud while the device is still powered does not resend this data. Instead, a small reconnection message is sent to the cloud, which uses 135 bytes. + +Prior to 0.6.0, when the device was reset or woken from deep sleep, the cloud connection would be fully reinitialized, which meant resending the 4400 bytes of data. From 0.6.0, the device determines that a full reinitialization isn't needed and reuses the existing session, after validating that the local state matches what was last communicated to the cloud. Connecting to the cloud after reset or wake-up sends just a reconnect message, using 135 bytes of data. A key requirement for the device to be able to determine that the existing session can be reused is that the functions, variables and subscriptions are registered BEFORE connecting to the cloud. + +Registering functions and variables before connecting to the cloud is easily done using `SEMI_AUTOMATIC` mode: + +```cpp +// EXAMPLE USAGE +// Using SEMI_AUTOMATIC mode to get the lowest possible data usage by +// registering functions and variables BEFORE connecting to the cloud. +SYSTEM_MODE(SEMI_AUTOMATIC); + +void setup() { + // register cloudy things + Particle.function(....); + Particle.variable(....); + Particle.subscribe(....); + // etc... + // then connect + Particle.connect(); +} +``` +{{/if}} + + ### Particle.variable() Expose a *variable* through the Cloud so that it can be called with `GET /v1/devices/{DEVICE_ID}/{VARIABLE}`. @@ -49,13 +82,13 @@ void loop() } ``` -Currently, up to 10 cloud variables may be defined and each variable name is limited to a maximum of 12 characters. +Up to 20 cloud variables may be registered and each variable name is limited to a maximum of 12 characters. It is fine to call this function when the cloud is disconnected - the variable will be registered next time the cloud is connected. Prior to 0.4.7 firmware, variables were defined with an additional 3rd parameter -to specify the data type of the variable. From 0.4.7 onwards, the system can +to specify the data type of the variable. From 0.4.7 onward, the system can infer the type from the actual variable. Additionally, the variable address was passed via the address-of operator (`&`). With 0.4.7 and newer, this is no longer required. @@ -106,19 +139,19 @@ my name is particle Expose a *function* through the Cloud so that it can be called with `POST /v1/devices/{DEVICE_ID}/{FUNCTION}`. ```cpp -// SYNTAX TO REGISTER A CLOUD FUNCTION +// SYNTAX bool success = Particle.function("funcKey", funcName); -// ^ -// | -// (max of 12 characters long) + +// Cloud functions must return int and take one String +int funcName(String extra) { + return 0; +} ``` -Currently the application supports the creation of up to 4 different cloud functions. +Up to 15 cloud functions may be registered and each function name is limited to a maximum of 12 characters. In order to register a cloud function, the user provides the `funcKey`, which is the string name used to make a POST request and a `funcName`, which is the actual name of the function that gets called in your app. The cloud function can return any integer; `-1` is commonly used for a failed function call. -The length of the `funcKey` is limited to a max of 12 characters. If you declare a function name longer than 12 characters the function will not be registered. - A cloud function is set up to take one argument of the [String](#string-class) datatype. This argument length is limited to a max of 63 characters. ```cpp @@ -305,23 +338,37 @@ data: {"data":"23:23:44","ttl":"60","published_at":"2014-05-28T19:20:34.638Z","d {{#if electron}} *`NO_ACK` flag* -Unless specified otherwise, events sent to the cloud are sent as a reliable message. The Electoron waits for -acknowledgement from the cloud that the event has been recieved, resending the event in the background up to 3 times before giving up. +Unless specified otherwise, events sent to the cloud are sent as a reliable message. The Electron waits for +acknowledgement from the cloud that the event has been received, resending the event in the background up to 3 times before giving up. -The `NO_ACK` flag disables this acknoweldge/retry behavior and sends the event only once. This reduces data consumption per event, with the possibility that the event may not reach the cloud. +The `NO_ACK` flag disables this acknowledge/retry behavior and sends the event only once. This reduces data consumption per event, with the possibility that the event may not reach the cloud. -For example, the `NO_ACK` flag could be useful when many events are sent (such as sensor readings) and the occaisonal lost event can be tolerated. +For example, the `NO_ACK` flag could be useful when many events are sent (such as sensor readings) and the occasional lost event can be tolerated. ```C++ // SYNTAX -int temperature = sensor.readTemperature(); // by way of example, not part of the API +int temperature = sensor.readTemperature(); // by way of example, not part of the API Particle.publish("t", temperature, NO_ACK); Particle.publish("t", temperature, PRIVATE, NO_ACK); Particle.publish("t", temperature, ttl, PRIVATE, NO_ACK); ``` -{{/if}} +{{/if}} {{!-- electron --}} + +_`WITH_ACK` flag_ + +_Since 0.6.1_ + +This flag causes `Particle.publish()` to return only after receiving an acknowledgement that the published event has been received by the Cloud. + +```C++ +// SYNTAX + +Particle.publish("motion-detected", NULL, WITH_ACK); +Particle.publish("motion-detected", NULL, PRIVATE, WITH_ACK); +Particle.publish("motion-detected", NULL, ttl, PRIVATE, WITH_ACK); +``` ### Particle.subscribe() @@ -469,8 +516,12 @@ void loop() { } ``` -While this function will disconnect from the Cloud, it will keep the connection to the {{#unless electron}}Wi-Fi network. If you would like to completely deactivate the Wi-Fi module, use [`WiFi.off()`](#off-).{{/unless}}{{#if electron}}Cellular network. If you would like to completely deactivate the Cellular module, use [`Cellular.off()`](#off-).{{/if}} - +{{#if has-wifi}} +While this function will disconnect from the Cloud, it will keep the connection to the Wi-Fi network. If you would like to completely deactivate the Wi-Fi module, use [`WiFi.off()`](#off-). +{{/if}} +{{#if has-cellular}} +While this function will disconnect from the Cloud, it will keep the connection to the Cellular network. If you would like to completely deactivate the Cellular module, use [`Cellular.off()`](#off-). +{{/if}} **NOTE:* When the device is disconnected, many features are not possible, including over-the-air updates, reading Particle.variables, and calling Particle.functions. @@ -500,7 +551,7 @@ void loop() { } ``` -{{#if electron}} +{{#if has-cellular}} ### Particle.keepAlive() Sets the duration between keep alive messages used to maintain the connection to the cloud. @@ -511,13 +562,15 @@ Particle.keepAlive(23 * 60); // send a ping every 23 minutes ``` A keep alive is used to implement "UDP hole punching" which helps maintain the connection from the cloud to the device. -Should a device becomes unreachable from the cloud (such as a timed out function call or variable get), +Should a device becomes unreachable from the cloud (such as a timed out function call or variable get), one possible cause of this is that the keep alives have not been sent often enough. The keep alive duration varies by mobile network operator. The default keepalive is set to 23 minutes, which is sufficient to maintain the connection on Particle SIM cards. 3rd party SIM cards will need to determine the appropriate keep alive value. +**Note:** Each keep alive ping consumes 122 bytes of data (61 bytes sent, 61 bytes received). -{{/if}} + +{{/if}} {{!-- has-cellular --}} ### Particle.process() @@ -529,7 +582,12 @@ Runs the background loop. This is the public API for the former internal functio and processes any messages that have come in. It also sends keep-alive pings to the Cloud, so if it's not called frequently, the connection to the Cloud may be lost. -Even in non-cloud-bound applications it can still be advisable to call `Particle.process()` to explicitly provide some processor time to the {{#unless electron}}Wi-Fi module (e.g. immediately after `WiFi.ready()` to update system variables).{{/unless}}{{#if electron}}Cellular module (e.g. immediately after `Cellular.ready()` to update system variables).{{/if}} +{{#if has-wifi}} +Even in non-cloud-bound applications it can still be advisable to call `Particle.process()` to explicitly provide some processor time to the Wi-Fi module (e.g. immediately after `WiFi.ready()` to update system variables). +{{/if}} +{{#if has-cellular}} +Even in non-cloud-bound applications it can still be advisable to call `Particle.process()` to explicitly provide some processor time to the Cellular module (e.g. immediately after `Cellular.ready()` to update system variables). +{{/if}} ```cpp void setup() { @@ -573,6 +631,120 @@ void loop() { Note that this function sends a request message to the Cloud and then returns. The time on the device will not be synchronized until some milliseconds later when the Cloud responds with the current time between calls to your loop. +See [`Particle.syncTimeDone()`](#particle-synctimedone-), [`Particle.timeSyncedLast()`](#particle-timesyncedlast-), [`Time.isValid()`](#isvalid-) and [`Particle.syncTimePending()`](#particle-synctimepending-) for information on how to wait for request to be finished. + +### Particle.syncTimeDone() + +_Since 0.6.1_ + +Returns `true` if there is no `syncTime()` request currently pending or there is no active connection to Particle Cloud. Returns `false` when there is a pending `syncTime()` request. + +```C++ +// SYNTAX +Particle.syncTimeDone(); + +// RETURNS +// boolean (true or false) +``` + +```C++ +// EXAMPLE + +void loop() +{ + // Request time synchronization from the Particle Cloud + Particle.syncTime(); + // Wait until {{device}} receives time from Particle Cloud (or connection to Particle Cloud is lost) + waitUntil(Particle.syncTimeDone); + // Print current time + Serial.println(Time.timeStr()); +} +``` + +See also [`Particle.timeSyncedLast()`](#particle-timesyncedlast-) and [`Time.isValid()`](#isvalid-). + +### Particle.syncTimePending() + +_Since 0.6.1_ + +Returns `true` if there a `syncTime()` request currently pending. Returns `false` when there is no `syncTime()` request pending or there is no active connection to Particle Cloud. + +```C++ +// SYNTAX +Particle.syncTimePending(); + +// RETURNS +// boolean (true or false) +``` + +```C++ +// EXAMPLE + +void loop() +{ + // Request time synchronization from the Particle Cloud + Particle.syncTime(); + // Wait until {{device}} receives time from Particle Cloud (or connection to Particle Cloud is lost) + while(Particle.syncTimePending()) + { + // + // Do something else + // + + Particle.process(); + } + // Print current time + Serial.println(Time.timeStr()); +} +``` + +See also [`Particle.timeSyncedLast()`](#particle-timesyncedlast-) and [`Time.isValid()`](#isvalid-). + +### Particle.timeSyncedLast() + +_Since 0.6.1_ + +Used to check when time was last synchronized with Particle Cloud. + +```C++ +// SYNTAX +Particle.timeSyncedLast(); +Particle.timeSyncedLast(timestamp); +``` + +Returns the number of milliseconds since the device began running the current program when last time synchronization with Particle Cloud was performed. + +This function takes one optional argument: +- `timestamp`: `time_t` variable that will contain a UNIX timestamp received from Particle Cloud during last time synchronization + +```C++ +// EXAMPLE +#define ONE_DAY_MILLIS (24 * 60 * 60 * 1000) + +void loop() { + time_t lastSyncTimestamp; + unsigned long lastSync = Particle.timeSyncedLast(lastSyncTimestamp); + if (millis() - lastSync > ONE_DAY_MILLIS) { + unsigned long cur = millis(); + Serial.printlnf("Time was last synchronized %lu milliseconds ago", millis() - lastSync); + if (lastSyncTimestamp > 0) + { + Serial.print("Time received from Particle Cloud was: "); + Serial.println(Time.timeStr(lastSyncTimestamp)); + } + // Request time synchronization from Particle Cloud + Particle.syncTime(); + // Wait until {{device}} receives time from Particle Cloud (or connection to Particle Cloud is lost) + waitUntil(Particle.syncTimeDone); + // Check if synchronized successfully + if (Particle.timeSyncedLast() >= cur) + { + // Print current time + Serial.println(Time.timeStr()); + } + } +} +``` ### Get Public IP @@ -640,7 +812,7 @@ void setup() { } ``` -{{#unless electron}} +{{#if has-wifi}} ## WiFi ### on() @@ -667,10 +839,10 @@ It's possible to call `WiFi.connect()` without entering listening mode in the ca ```cpp // SYNTAX -WiFi.connect(WIFI_CONNECT_NO_LISTEN); +WiFi.connect(WIFI_CONNECT_SKIP_LISTEN); ``` -If there are no credentials then the call does nothing other than turn on the WiFi module. +If there are no credentials then the call does nothing other than turn on the Wi-Fi module. ### disconnect() @@ -700,7 +872,7 @@ This function will return `true` once the device is connected to the network and WiFi.ready(); ``` -{{#if photon}} +{{#if has-wifi-antenna-switch}} ### selectAntenna() Selects which antenna the device should connect to Wi-Fi with and remembers that @@ -745,10 +917,7 @@ void loop() { } ``` - - - -{{/if}} +{{/if}} {{!-- has-wifi-antenna-switch --}} ### listen() @@ -798,6 +967,58 @@ It will return `false` when the device is not in listening mode. {{/if}} +### setListenTimeout() + +_Since 0.6.1_ + +```cpp +// SYNTAX +WiFi.setListenTimeout(seconds); +``` + +`WiFi.setListenTimeout(seconds)` is used to set a timeout value for Listening Mode. Values are specified in `seconds`, and 0 disables the timeout. By default, Wi-Fi devices do not have any timeout set (seconds=0). As long as interrupts are enabled, a timer is started and running while the device is in listening mode (WiFi.listening()==true). After the timer expires, listening mode will be exited automatically. If WiFi.setListenTimeout() is called while the timer is currently in progress, the timer will be updated and restarted with the new value (e.g. updating from 10 seconds to 30 seconds, or 10 seconds to 0 seconds (disabled)). {{#unless core}}**Note:** Enabling multi-threaded mode with SYSTEM_THREAD(ENABLED) will allow user code to update the timeout value while Listening Mode is active.{{/unless}} {{#if core}}Because listening mode blocks your application code on the Core, this command should be avoided in loop(). It can be used with the STARTUP() macro or in setup() on the Core. +It will always return `false`. + +This setting is not persistent in memory if the {{device}} is rebooted. +{{/if}} + +```cpp +// EXAMPLE +// If desired, use the STARTUP() macro to set the timeout value at boot time. +STARTUP(WiFi.setListenTimeout(60)); // set listening mode timeout to 60 seconds + +void setup() { + // your setup code +} +{{#unless core}} +void loop() { + // update the timeout later in code based on an expression + if (disableTimeout) WiFi.setListenTimeout(0); // disables the listening mode timeout +} +{{/unless}} +``` + + +### getListenTimeout() + +_Since 0.6.1_ + +```cpp +// SYNTAX +uint16_t seconds = WiFi.getListenTimeout(); +``` + +`WiFi.getListenTimeout()` is used to get the timeout value currently set for Listening Mode. Values are returned in (uint16_t)`seconds`, and 0 indicates the timeout is disabled. By default, Wi-Fi devices do not have any timeout set (seconds=0). + +```cpp +// EXAMPLE +void setup() { + Serial.begin(); + Serial.println(WiFi.getListenTimeout()); +} +``` + + ### setCredentials() Allows the application to set credentials for the Wi-Fi network from within the code. These credentials will be added to the device's memory, and the device will automatically attempt to connect to this network in the future. @@ -832,13 +1053,13 @@ When the Photon used with hidden or offline networks, the security cipher is als WiFi.setCredentials("SSID", "PASSWORD", WPA2, WLAN_CIPHER_AES)); ``` -{{/if}} +{{/if}} {{!-- photon --}} -**Note:** In order for `WiFi.setCredentials()` to work, the WiFi module needs to be on (if switched off or disabled via non_AUTOMATIC SYSTEM_MODEs call `WiFi.on()`). +**Note:** In order for `WiFi.setCredentials()` to work, the Wi-Fi module needs to be on (if switched off or disabled via non_AUTOMATIC SYSTEM_MODEs call `WiFi.on()`). ### getCredentials() -*Since 0.4.9.* +_Since 0.4.9_ Lists the Wi-Fi networks with credentials stored on the device. Returns the number of stored networks. @@ -1217,11 +1438,12 @@ A note on switching between static and dynamic IP. If static IP addresses have b by the system after calling `WiFi.useDynamicIP()`, and so are available for use next time `WiFi.useStaticIP()` is called, without needing to be reconfigured using `WiFi.setStaticIP()` -{{/if}} -{{/unless}} +{{/if}} {{!-- photon --}} +{{/if}} {{!-- has-wifi --}} -{{#if photon}} + +{{#if has-softap}} ## SoftAP HTTP Pages _Since 0.5.0_ @@ -1242,32 +1464,32 @@ SoftAP HTTP Pages is presently an advanced feature, requiring moderate C++ knowl void myPages(const char* url, ResponseCallback* cb, void* cbArg, Reader* body, Writer* result, void* reserved); STARTUP(softap_set_application_page_handler(myPages, nullptr)); -``` +``` The `softap_set_application_page_handler` is set during startup. When the system is in setup mode, and a request is made for an unknown URL, the system calls the page handler function provided by the application (here, `myPages`.) -The page handler function is called whenver an unknown URL is requested. It is called with these parameters: +The page handler function is called whenever an unknown URL is requested. It is called with these parameters: - `url`: the path of the file requested by the client. It doesn't include the server name or port. Examples: `/index`, `/someimage.jpg`. - `cb`: a response callback - this is used by the application to indicate the type of HTTP response, such as 200 (OK) or 404 (not found). More on this below. -- `cbArg`: data that should be passed as the first parameter to the callback function `cb`. +- `cbArg`: data that should be passed as the first parameter to the callback function `cb`. - `body`: a reader object that the page handler uses to retrieve the HTTP request body - `result`: a writer object that the page handler uses to write the HTTP response body - `reserved`: reserved for future expansion. Will be equal to `nullptr` and can be ignored. -The application MUST call the page callback function `cb` to provide a response for the requested page. If the requested page url isn't recognized by the application, then a 404 response should be sent, as described below. +The application MUST call the page callback function `cb` to provide a response for the requested page. If the requested page url isn't recognized by the application, then a 404 response should be sent, as described below. ### The page callback function -When your page handler function is called, the system passes a result callback function as the `cb` parameter. +When your page handler function is called, the system passes a result callback function as the `cb` parameter. The callback function takes these parameters: -- `cbArg`: this is the `cbArg` parameter passed to your page callback function. It's internal state used by the HTTP server. +- `cbArg`: this is the `cbArg` parameter passed to your page callback function. It's internal state used by the HTTP server. - `flags`: presently unused. Set to 0. - `status`: the HTTP status code, as an integer, such as 200 for `OK`, or 404 for `page not found`. -- `mime-type`: the mime-type of the response as a string, such as `text/html` or `application/javascript`. -- `header`: an optional pointer to a `Header` that is added to the response sent to the client. +- `mime-type`: the mime-type of the response as a string, such as `text/html` or `application/javascript`. +- `header`: an optional pointer to a `Header` that is added to the response sent to the client. For example, to send a "not found" error for a page that is not recognized, your application code would call @@ -1280,13 +1502,13 @@ cb(cbArg, 0, 404, "text/plain", nullptr); ### Retrieving the request data When the HTTP request contains a request body (such as with a POST request), the `Reader` object provided by the `body` parameter can be used -to retrieve the request data. +to retrieve the request data. ```cpp // EXAMPLE if (body->bytes_left) { - char* data = body->read_as_string(); + char* data = body->fetch_as_string(); // handle the body data dostuff(data); // free the data! IMPORTANT! @@ -1297,7 +1519,7 @@ if (body->bytes_left) { ### Sending a response -When sending a page, the page function responds with a HTTP 200 code, meaning the content was found, followed by the page data. +When sending a page, the page function responds with a HTTP 200 code, meaning the content was found, followed by the page data. ``` // EXAMPLE - send a page @@ -1313,7 +1535,7 @@ if (!stricmp(url, '/helloworld') { ### The default page When a browser requests the default page (`http://192.168.0.1/`) the system internally redirects this to `/index` so that it can be handled -by the application. +by the application. The application may provide an actual page at `/index` or redirect to another page if the application pefers to have another page as its launch page. @@ -1418,7 +1640,7 @@ void myPage(const char* url, ResponseCallback* cb, void* cbArg, Reader* body, Wr STARTUP(softap_set_application_page_handler(myPage, nullptr)); ``` -{{/if}} +{{/if}} {{!-- has-softap --}} {{#if electron}} @@ -1517,6 +1739,56 @@ or the setup button has been held for 3 seconds, when the RGB LED should be blin It will return `false` when the device is not in listening mode. +### setListenTimeout() + +_Since 0.6.1_ + +```cpp +// SYNTAX +Cellular.setListenTimeout(seconds); +``` + +`Cellular.setListenTimeout(seconds)` is used to set a timeout value for Listening Mode. Values are specified in `seconds`, and 0 disables the timeout. By default, Cellular devices have a 5 minute timeout set (seconds=300). As long as interrupts are enabled, a timer is started and running while the device is in listening mode (Cellular.listening()==true). After the timer expires, listening mode will be exited automatically. If Cellular.setListenTimeout() is called while the timer is currently in progress, the timer will be updated and restarted with the new value (e.g. updating from 10 seconds to 30 seconds, or 10 seconds to 0 seconds (disabled)). **Note:** Enabling multi-threaded mode with SYSTEM_THREAD(ENABLED) will allow user code to update the timeout value while Listening Mode is active. + +This setting is not persistent in memory if the {{device}} is rebooted. + +```cpp +// EXAMPLE +// If desired, use the STARTUP() macro to set the timeout value at boot time. +STARTUP(Cellular.setListenTimeout(60)); // set listening mode timeout to 60 seconds + +void setup() { + // your setup code +} + +void loop() { + // update the timeout later in code based on an expression + if (disableTimeout) Cellular.setListenTimeout(0); // disables the listening mode timeout +} +``` + + +### getListenTimeout() + +_Since 0.6.1_ + +```cpp +// SYNTAX +uint16_t seconds = Cellular.getListenTimeout(); +``` + +`Cellular.getListenTimeout()` is used to get the timeout value currently set for Listening Mode. Values are returned in (uint16_t)`seconds`, and 0 indicates the timeout is disabled. By default, Cellular devices have a 5 minute timeout set (seconds=300). + +```cpp +// EXAMPLE +void setup() { + Serial.begin(); + Serial.println(Cellular.getListenTimeout()); +} +``` + + + ### setCredentials() **Note**: `Cellular.setCredentials()` is not currently enabled, however read on to find out how to use the cellular_hal to do the same thing with `cellular_credentials_set()`. @@ -1735,7 +2007,7 @@ void loop() bool Cellular.getBandSelect(CellularBand &data_get); ### getBandAvailable() -*Since 0.5.0.* +_Since 0.5.0._ Gets the cellular bands currently available in the modem. `Bands` are the carrier frequncies used to communicate with the cellular network. Some modems have 2 bands available (U260/U270) and others have 4 bands (G350). @@ -1792,7 +2064,7 @@ else { ``` ### getBandSelect() -*Since 0.5.0.* +_Since 0.5.0_ Gets the cellular bands currently set in the modem. `Bands` are the carrier frequncies used to communicate with the cellular network. @@ -1823,7 +2095,7 @@ else { ``` ### setBandSelect() -*Since 0.5.0.* +_Since 0.5.0_ Sets the cellular bands currently set in the modem. `Bands` are the carrier frequncies used to communicate with the cellular network. @@ -1895,7 +2167,7 @@ else { ``` ### localIP() -*Since 0.5.0.* +_Since 0.5.0_ `Cellular.localIP()` returns the local (private) IP address assigned to the device as an `IPAddress`. @@ -2009,10 +2281,10 @@ There are 13 different enumerated AT command responses passed by the system into - `TYPE_TEXT` = 0x500000 - `TYPE_ABORTED` = 0x600000 -{{/if}} +{{/if}} {{!-- electron --}} -{{#if electron}} +{{#if has-fuel-gauge}} ## FuelGauge The on-board Fuel Gauge allows you to monitor the battery voltage, state of charge and set low voltage battery thresholds. Use an instance of the `FuelGauge` library to call the various fuel gauge functions. @@ -2068,7 +2340,7 @@ Serial.println( fuel.getSoC() ); ### wakeup() `void wakeup();` -{{/if}} +{{/if}} {{!-- has-fuel-gauge --}} ## Input/Output @@ -2153,7 +2425,7 @@ void loop() } ``` -**Note:** All GPIO pins (`D0`..`D7`, `A0`..`A7`, `DAC`, `WKP`, `RX`, `TX`) can be used as long they are not used otherwise (e.g. as `Serial1` `RX`/`TX`). +**Note:** All GPIO pins (`A0`..`A7`, {{#if electron}}`B0`..`B5`, `C0`..`C5`, {{/if}}`D0`..`D7`, `DAC`, `WKP`, `RX`, `TX`) can be used as long they are not used otherwise (e.g. as `Serial1` `RX`/`TX`). ### digitalRead() @@ -2187,7 +2459,7 @@ void loop() } ``` -**Note:** All GPIO pins (`D0`..`D7`, `A0`..`A7`, `DAC`, `WKP`, `RX`, `TX`) can be used as long they are not used otherwise (e.g. as `Serial1` `RX`/`TX`). +**Note:** All GPIO pins (`A0`..`A7`, {{#if electron}}`B0`..`B5`, `C0`..`C5`, {{/if}}`D0`..`D7`, `DAC`, `WKP`, `RX`, `TX`) can be used as long they are not used otherwise (e.g. as `Serial1` `RX`/`TX`). ### analogWrite() (PWM) @@ -2204,8 +2476,8 @@ analogWrite(pin, value, frequency); `analogWrite()` takes two or three arguments: - `pin`: the number of the pin whose value you wish to set -- `value`: the duty cycle: between 0 (always off) and 255 (always on). -- `frequency`: the PWM frequency: between 1 Hz and 65535 Hz (default 500 Hz). +- `value`: the duty cycle: between 0 (always off) and 255 (always on). _Since 0.6.0:_ between 0 and 255 (default 8-bit resolution) or `2^(analogWriteResolution(pin)) - 1` in general. +- `frequency`: the PWM frequency: between 1 Hz and 65535 Hz (default 500 Hz). _Since 0.6.0:_ between 1 Hz and `analogWriteMaxFrequency(pin)`. **NOTE:** `pinMode(pin, OUTPUT);` is required before calling `analogWrite(pin, value);` or else the `pin` will not be initialized as a PWM output and set to the desired duty cycle. @@ -2243,9 +2515,61 @@ The PWM frequency must be the same for pins in the same timer group. - On the P1, the timer groups are D0/D1, D2/D3/A4/A5/P1S0/P1S1, WKP, RX/TX. - On the Electron, the timer groups are D0/D1/C4/C5, D2/D3/A4/A5/B2/B3, WKP, RX/TX, B0/B1. -**NOTE:** When used with PWM capable pins, the `analogWrite()` function sets up these pins as PWM only. {{#unless core}}This function operates differently when used with the [`Analog Output (DAC)`](#analog-output-dac-) pins.{{/unless}} +**NOTE:** When used with PWM capable pins, the `analogWrite()` function sets up these pins as PWM only. {{#if has-dac}}This function operates differently when used with the [`Analog Output (DAC)`](#analog-output-dac-) pins.{{/if}} -{{#unless core}} +{{#if has-pwm}} + +{{#if has-dac}} +### analogWriteResolution() (PWM and DAC) +{{else}} +### analogWriteResolution() (PWM) +{{/if}} + +_Since 0.6.0_ + +Sets or retrieves the resolution of `analogWrite()` function of a particular pin. + +`analogWriteResolution()` takes one or two arguments: + +- `pin`: the number of the pin whose resolution you wish to set or retrieve +- `resolution`: (optional) resolution in bits. The value can range from 2 to 31 bits. If the resolution is not supported, it will not be applied. + +`analogWriteResolution()` returns currently set resolution. + +```C++ +// EXAMPLE USAGE +pinMode(D1, OUTPUT); // sets the pin as output +analogWriteResolution(D1, 12); // sets analogWrite resolution to 12 bits +analogWrite(D1, 3000, 1000); // 3000/4095 = ~73% duty cycle at 1kHz +``` + +{{#if has-dac}} +**NOTE:** DAC pins `DAC1` (`A6`) and `DAC2` (`A3`) support only either 8-bit or 12-bit (default) resolutions. +{{/if}} + +**NOTE:** The resolution also affects maximum frequency that can be used with `analogWrite()`. The maximum frequency allowed with current resolution can be checked by calling `analogWriteMaxFrequency()`. + +### analogWriteMaxFrequency() (PWM) + +_Since 0.6.0_ + +Returns maximum frequency that can be used with `analogWrite()` on this pin. + +`analogWriteMaxFrequency()` takes one argument: + +- `pin`: the number of the pin + +```C++ +// EXAMPLE USAGE +pinMode(D1, OUTPUT); // sets the pin as output +analogWriteResolution(D1, 12); // sets analogWrite resolution to 12 bits +int maxFreq = analogWriteMaxFrequency(D1); +analogWrite(D1, 3000, maxFreq / 2); // 3000/4095 = ~73% duty cycle +``` + +{{/if}} {{!-- has-pwm --}} + +{{#if has-dac}} ### Analog Output (DAC) The Photon and Electron support true analog output on pins DAC (`DAC1` or `A6` in code) and A3 (`DAC2` or `A3` in code). Using `analogWrite(pin, value)` @@ -2262,13 +2586,17 @@ pinMode(DAC1, OUTPUT); analogWrite(DAC1, 1024); // sets DAC pin to an output voltage of 1024/4095 * 3.3V = 0.825V. ``` -{{/unless}} +{{/if}} {{!-- has-dac --}} + +{{#if has-adc}} ### analogRead() (ADC) Reads the value from the specified analog pin. The device has 8 channels (A0 to A7) with a 12-bit resolution. This means that it will map input voltages between 0 and 3.3 volts into integer values between 0 and 4095. This yields a resolution between readings of: 3.3 volts / 4096 units or, 0.0008 volts (0.8 mV) per unit. -**Note**: do *not* set the pinMode() with `analogRead()`. The pinMode() is automatically set to AN_INPUT the first time analogRead() is called for a particular analog pin. If you explicitly set a pin to INPUT or OUTPUT after that first use of analogRead(), it will not attempt to switch it back to AN_INPUT the next time you call analogRead() for the same analog pin. This will create incorrect analog readings. +_Before 0.5.3_ **Note**: do *not* set the pinMode() with `analogRead()`. The pinMode() is automatically set to AN_INPUT the first time analogRead() is called for a particular analog pin. If you explicitly set a pin to INPUT or OUTPUT after that first use of analogRead(), it will not attempt to switch it back to AN_INPUT the next time you call analogRead() for the same analog pin. This will create incorrect analog readings. + +_Since 0.5.3_ **Note:** you do not need to set the pinMode() with analogRead(). The pinMode() is automatically set to AN_INPUT any time analogRead() is called for a particular analog pin, if that pin is set to a pinMode other than AN_INPUT. If you explicitly set a pin to INPUT, INPUT_PULLUP, INPUT_PULLDOWN or OUTPUT before using analogRead(), it will switch it back to AN_INPUT before taking the reading. If you use digitalRead() afterwards, it will automatically switch the pinMode back to whatever you originally explicitly set it to. ```C++ // SYNTAX @@ -2326,6 +2654,8 @@ On the Core, this parameter can be one of the following values: * ADC_SampleTime_144Cycles: Sample time equal to 144 cycles * ADC_SampleTime_480Cycles: Sample time equal to 480 cycles +{{/if}} {{!-- has-adc --}} + ## Low Level Input/Output The Input/Ouput functions include safety checks such as making sure a pin is set to OUTPUT when doing a digitalWrite() or that the pin is not being used for a timer function. These safety measures represent good coding and system design practice. @@ -2640,7 +2970,7 @@ loop() { ### pulseIn() -*Since 0.4.7.* +_Since 0.4.7_ Reads a pulse (either HIGH or LOW) on a pin. For example, if value is HIGH, pulseIn() waits for the pin to go HIGH, starts timing, then waits for the pin to go LOW and stops timing. Returns the length of the pulse in microseconds or 0 if no complete pulse was received within the timeout. @@ -2686,7 +3016,8 @@ void loop() */ ``` -{{#if electron}} +{{#if has-pmic}} + ## PMIC (Power Managment IC) *Note*: This is advanced IO and for experienced users. This @@ -2889,29 +3220,53 @@ by the system firmware. #### getNTCFault() `byte getNTCFault();` -{{/if}} +{{/if}} {{!-- has-pmic --}} ## Serial -Used for communication between the device and a computer or other devices. The device has two serial channels: +Used for communication between the device and a computer or other devices. The device has {{#if electron}}four{{else}}two{{/if}} hardware (USART) serial channels and {{#unless core}}two{{else}}one{{/unless}} USB serial channel{{#unless core}}s{{else}}{{/unless}}. +{{#unless raspberry-pi}} `Serial:` This channel communicates through the USB port and when connected to a computer, will show up as a virtual COM port. +{{else}} +`Serial:` This channel communicates between the terminal and the firmware running. It uses standard input and standard output. +{{/unless}} + +```C++ +// EXAMPLE USAGE +void setup() +{ + Serial.begin(); + Serial.println("Hello World!"); +} +``` +{{#if has-usb-serial1}} +`USBSerial1`: _Since 0.6.0_ This channel communicates through the USB port and when connected to a computer, will show up as a second virtual COM port. This channel is disabled by default. +{{/if}} `Serial1:` This channel is available via the device's TX and RX pins. +{{#if raspberry-pi}} +**IMPORTANT**: Support for `Serial1` is not complete for the Raspberry Pi so `Serial1` never returns any data. +{{/if}} + +{{#if has-serial2}} + {{#if core}} `Serial2:` This channel is optionally available via the device's D1(TX) and D0(RX) pins. To use Serial2, add `#include "Serial2/Serial2.h"` near the top of your app's main code file. To use the TX/RX (Serial1) or D1/D0 (Serial2) pins to communicate with your personal computer, you will need an additional USB-to-serial adapter. To use them to communicate with an external TTL serial device, connect the TX pin to your device's RX pin, the RX to your device's TX pin, and the ground of your Core to your device's ground. -{{/if}} -{{#unless core}} +{{else}} + `Serial2:` This channel is optionally available via the device's RGB Green (TX) and Blue (RX) LED pins. The Blue and Green current limiting resistors should be removed. To use Serial2, add #include "Serial2/Serial2.h" near the top of your app's main code file. If the user enables Serial2, they should also consider using RGB.onChange() to move the RGB functionality to an external RGB LED on some PWM pins. -{{/unless}} +{{/if}} -{{#if electron}} +{{/if}} {{!-- has-serial2 --}} + +{{#if has-serial4-5}} `Serial4:` This channel is optionally available via the Electron's C3(TX) and C2(RX) pins. To use Serial4, add `#include "Serial4/Serial4.h"` near the top of your app's main code file. `Serial5:` This channel is optionally available via the Electron's C1(TX) and C0(RX) pins. To use Serial5, add `#include "Serial5/Serial5.h"` near the top of your app's main code file. @@ -2919,84 +3274,169 @@ If the user enables Serial2, they should also consider using RGB.onChange() to m ```C++ // EXAMPLE USAGE -// Include the appropriate header file for Serial2{{#if electron}}, Serial4, or Serial5{{/if}} +{{#if has-serial2}} +// IMPORTANT: Include the header file for Serial2 #include "Serial2/Serial2.h" -{{#if electron}} +{{/if}} +{{#if has-serial4-5}} +// IMPORTANT: Include the header file for Serial4/5 #include "Serial4/Serial4.h" #include "Serial5/Serial5.h" {{/if}} void setup() { + Serial1.begin(9600); +{{#if has-serial2}} Serial2.begin(9600); -{{#if electron}} +{{/if}} +{{#if has-serial4-5}} Serial4.begin(9600); Serial5.begin(9600); {{/if}} + Serial1.println("Hello World!"); +{{#if has-serial2}} Serial2.println("Hello World!"); -{{#if electron}} +{{/if}} +{{#if has-serial4-5}} Serial4.println("Hello World!"); Serial5.println("Hello World!"); {{/if}} } ``` -To use the hardware serial pins of (Serial1/2{{#if electron}}/4/5{{/if}}) to communicate with your personal computer, you will need an additional USB-to-serial adapter. To use them to communicate with an external TTL serial device, connect the TX pin to your device's RX pin, the RX to your device's TX pin, and the ground of your Core/Photon/Electron to your device's ground. +To use the hardware serial pins of (Serial1{{#if has-serial2}}/2{{/if}}{{#if has-serial4-5}}/4/5{{/if}}) to communicate with your personal computer, you will need an additional USB-to-serial adapter. To use them to communicate with an external TTL serial device, connect the TX pin to your device's RX pin, the RX to your device's TX pin, and the ground of your {{device}} to your device's ground. -**NOTE:** Please take into account that the voltage levels on these pins operate at 0V to 3.3V and should not be connected directly to a computer's RS232 serial port which operates at +/- 12V and will damage the Core/Photon/Electron. +**NOTE:** Please take into account that the voltage levels on these pins operate at 0V to 3.3V and should not be connected directly to a computer's RS232 serial port which operates at +/- 12V and will damage the {{device}}. ### begin() -Sets the data rate in bits per second (baud) for serial data transmission. For communicating with the computer, use one of these rates: 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, or 115200. You can, however, specify other rates - for example, to communicate over pins TX and RX with a component that requires a particular baud rate. +_Available on Serial, {{#if has-usb-serial1}}USBSerial1, {{/if}}Serial1{{#if has-serial2}}, Serial2{{/if}}{{#if has-serial4-5}}, Serial4, Serial5{{/if}}._ -**NOTE:** The data rate for the USB device `Serial` is ignored, as USB has its own negotiated speed. Setting speed to 9600 is safe for the USB device. Setting the port on the Host computer to 14400 baud will cause the Photon or Electron to go into DFU mode while 28800 will allow a YMODEM download of firmware. +Enables serial channel with specified configuration. -_Since 0.5.0_ +As of 0.5.0 firmware, 28800 baudrate set by the Host on `Serial` will put the device in Listening Mode, where a YMODEM download can be started by additionally sending an `f` character. + +{{#if has-usb-serial1}} +***NOTE*** _Since 0.6.0_: When `USBSerial1` is enabled by calling `USBSerial1.begin()` in `setup()` or during normal application execution, the device will quickly disconnect from Host and connect back with `USBSerial1` enabled. If such behavior is undesireable, `USBSerial1` may be enabled with `STARTUP()` macro, which will force the device to connect to the Host with both `Serial` and `USBSerial1` by default. + +```C++ +// EXAMPLE USAGE +STARTUP(USBSerial1.begin()); +void setup() +{ + while(!Serial.isConnected()) + Particle.process(); + Serial.println("Hello Serial!"); + + while(!USBSerial1.isConnected()) + Particle.process(); + USBSerial1.println("Hello USBSerial1!"); +} +``` +{{/if}} {{!-- has-usb-serial1 --}} + +When using hardware serial channels (Serial1, Serial2{{#if electron}}, Serial4, Serial5{{/if}}), the configuration of the serial channel may also specify the number of data bits, stop bits, parity, flow control and other settings. The default is SERIAL_8N1 (8 data bits, no parity and 1 stop bit) and does not need to be specified to achieve this configuration. To specify one of the following configurations, add one of these defines as the second parameter in the `begin()` function, e.g. `Serial1.begin(9600, SERIAL_8E1);` for 8 data bits, even parity and 1 stop bit. + +Pre-defined Serial configurations available: + +- `SERIAL_8N1` - 8 data bits, no parity, 1 stop bit (default) +- `SERIAL_8N2` - 8 data bits, no parity, 2 stop bits +- `SERIAL_8E1` - 8 data bits, even parity, 1 stop bit +- `SERIAL_8E2` - 8 data bits, even parity, 2 stop bits +- `SERIAL_8O1` - 8 data bits, odd parity, 1 stop bit +- `SERIAL_8O2` - 8 data bits, odd parity, 2 stop bits +- `SERIAL_9N1` - 9 data bits, no parity, 1 stop bit +- `SERIAL_9N2` - 9 data bits, no parity, 2 stop bits + +_Since 0.6.0_ -As of 0.5.0 firmware, 28800 baud set on the Host will put the device in Listening Mode, where a YMODEM download can be started by additionally sending an `f` character. +- `SERIAL_7O1` - 7 data bits, odd parity, 1 stop bit +- `SERIAL_7O2` - 7 data bits, odd parity, 1 stop bit +- `SERIAL_7E1` - 7 data bits, odd parity, 1 stop bit +- `SERIAL_7E2` - 7 data bits, odd parity, 1 stop bit +- `LIN_MASTER_13B` - 8 data bits, no parity, 1 stop bit, LIN Master mode with 13-bit break generation +- `LIN_SLAVE_10B` - 8 data bits, no parity, 1 stop bit, LIN Slave mode with 10-bit break detection +- `LIN_SLAVE_11B` - 8 data bits, no parity, 1 stop bit, LIN Slave mode with 11-bit break detection -The configuration of the serial channel may also specify the number of data bits, stop bits and parity. The default is SERIAL_8N1 (8 data bits, no parity and 1 stop bit) and does not need to be specified to achieve this configuration. To specify one of the following configurations, add one of these defines as the second parameter in the `begin()` function, e.g. `Serial.begin(9600, SERIAL_8E1);` for 8 data bits, even parity and 1 stop bit. +Alternatively, configuration may be constructed manually by ORing (`|`) the following configuration constants: -Serial configurations available: +Data bits: +- `SERIAL_DATA_BITS_7` - 7 data bits +- `SERIAL_DATA_BITS_8` - 8 data bits +- `SERIAL_DATA_BITS_9` - 9 data bits + +Stop bits: +- `SERIAL_STOP_BITS_1` - 1 stop bit +- `SERIAL_STOP_BITS_2` - 2 stop bits +- `SERIAL_STOP_BITS_0_5` - 0.5 stop bits +- `SERIAL_STOP_BITS_1_5` - 1.5 stop bits + +Parity: +- `SERIAL_PARITY_NO` - no parity +- `SERIAL_PARITY_EVEN` - even parity +- `SERIAL_PARITY_ODD` - odd parity + +{{#if core}} +Hardware flow control, available only on Serial1 (`CTS` - `A0`, `RTS` - `A1`): +{{/if}} +{{#unless core}} +Hardware flow control, available only on Serial2 (`CTS` - `A7`, `RTS` - `RGBR` ): +{{/unless}} +- `SERIAL_FLOW_CONTROL_NONE` - no flow control +- `SERIAL_FLOW_CONTROL_RTS` - RTS flow control +- `SERIAL_FLOW_CONTROL_CTS` - CTS flow control +- `SERIAL_FLOW_CONTROL_RTS_CTS` - RTS/CTS flow control + +LIN configuration: +- `LIN_MODE_MASTER` - LIN Master +- `LIN_MODE_SLAVE` - LIN Slave +- `LIN_BREAK_13B` - 13-bit break generation +- `LIN_BREAK_10B` - 10-bit break detection +- `LIN_BREAK_11B` - 11-bit break detection + +**NOTE:** LIN break detection may be enabled in both Master and Slave modes. -`SERIAL_8N1` - 8 data bits, no parity, 1 stop bit (default) -`SERIAL_8N2` - 8 data bits, no parity, 2 stop bits -`SERIAL_8E1` - 8 data bits, even parity, 1 stop bit -`SERIAL_8E2` - 8 data bits, even parity, 2 stop bits -`SERIAL_8O1` - 8 data bits, odd parity, 1 stop bit -`SERIAL_8O2` - 8 data bits, odd parity, 2 stop bits -`SERIAL_9N1` - 9 data bits, no parity, 1 stop bit -`SERIAL_9N2` - 9 data bits, no parity, 2 stop bits ```C++ // SYNTAX -Serial.begin(speed); // via USB port -Serial.begin(speed, config); // " +Serial.begin(); // via USB port + +{{#if has-usb-serial1}} +USBSerial1.begin(); // via USB port +{{/if}} Serial1.begin(speed); // via TX/RX pins Serial1.begin(speed, config); // " -Serial2.begin(speed); // on Core via - // D1(TX) and D0(RX) pins - // on Photon/Electron via - // RGB-LED green(TX) and - // RGB-LED blue (RX) pins +Serial1.begin(9600, SERIAL_9N1); // via TX/RX pins, 9600 9N1 mode +Serial1.begin(9600, SERIAL_DATA_BITS_8 | SERIAL_STOP_BITS_1_5 | SERIAL_PARITY_EVEN); // via TX/RX pins, 9600 8E1.5 + +{{#if has-serial2}} +#include "Serial2/Serial2.h" +Serial2.begin(speed); {{#if core}}// D1(TX) and D0(RX) pins{{else}}// RGB-LED green(TX) and blue (RX) pins{{/if}} Serial2.begin(speed, config); // " -{{#if electron}} +Serial2.begin(9600); // via RGB Green (TX) and Blue (RX) LED pins +Serial2.begin(9600, SERIAL_DATA_BITS_8 | SERIAL_STOP_BITS_1_5 | SERIAL_PARITY_EVEN); // via RGB Green (TX) and Blue (RX) LED pins, 9600 8E1.5 +{{/if}} {{!-- has-serial2 --}} +{{#if has-serial4-5}} + +#include "Serial4/Serial4.h" Serial4.begin(speed); // via C3(TX)/C2(RX) pins Serial4.begin(speed, config); // " +#include "Serial5/Serial5.h" Serial5.begin(speed); // via C1(TX)/C0(RX) pins Serial5.begin(speed, config); // " -{{/if}} +{{/if}} {{!-- has-serial4-5 --}} ``` -`speed`: parameter that specifies the baud rate *(long)* -`config`: parameter that specifies the number of data bits used, parity and stop bits *(long)* +Parameters: +- `speed`: parameter that specifies the baud rate *(long)* _(optional for `Serial` {{#if has-usb-serial1}}and `USBSerial1`{{/if}})_ +- `config`: parameter that specifies the number of data bits used, parity and stop bits *(long)* _(not used with `Serial` {{#if has-usb-serial1}}and `USBSerial1`{{/if}})_ -`begin()` does not return anything ```C++ // EXAMPLE USAGE @@ -3005,8 +3445,8 @@ void setup() Serial.begin(9600); // open serial over USB // On Windows it will be necessary to implement the following line: // Make sure your Serial Terminal app is closed before powering your device - // Now open your Serial Terminal, and hit any key to continue! - while(!Serial.available()) Particle.process(); + // Now open your Serial Terminal! + while(!Serial.isConnected()) Particle.process(); Serial1.begin(9600); // open serial over TX and RX pins @@ -3019,7 +3459,17 @@ void loop() {} ### end() -Disables serial communication, allowing the RX and TX pins to be used for general input and output. To re-enable serial communication, call `Serial1.begin()`. +_Available on Serial, {{#if has-usb-serial1}}USBSerial1, {{/if}}Serial1{{#if has-serial2}}, Serial2{{/if}}{{#if has-serial4-5}}, Serial4, Serial5{{/if}}._ + +Disables serial channel. + +When used with hardware serial channels (Serial1, Serial2{{#if electron}}, Serial4, Serial5{{/if}}), disables serial communication, allowing channel's RX and TX pins to be used for general input and output. To re-enable serial communication, call `SerialX.begin()`. + +{{#unless core}} +_Since 0.6.0_ + +When used with USB serial channels (`Serial`{{#if has-usb-serial1}} or `USBSerial1`{{/if}}), `end()` will cause the device to quickly disconnect from Host and connect back without the selected serial channel. +{{/unless}} ```C++ // SYNTAX @@ -3028,7 +3478,17 @@ Serial1.end(); ### available() -Get the number of bytes (characters) available for reading from the serial port. This is data that's already arrived and stored in the serial receive buffer (which holds 64 bytes). +_Available on Serial, {{#if has-usb-serial1}}USBSerial1, {{/if}}Serial1{{#if has-serial2}}, Serial2{{/if}}{{#if has-serial4-5}}, Serial4, Serial5{{/if}}._ + +Get the number of bytes (characters) available for reading from the serial port. This is data that's already arrived and stored in the serial receive buffer. + +The receive buffer size for hardware serial channels (Serial1, Serial2{{#if electron}}, Serial4, Serial5{{/if}}) is 64 bytes. + +{{#if has-usb-serial1}} +The receive buffer size for USB serial channels (Serial and USBSerial1) is 256 bytes. Also see [`acquireSerialBuffer`](#acquireserialbuffer-). +{{else}} +The receive buffer size for Serial is 64 bytes. +{{/if}} ```C++ // EXAMPLE USAGE @@ -3058,19 +3518,71 @@ void loop() ### availableForWrite() -_Since 0.4.9. Available on Serial1, Serial2, etc._ +_Since 0.4.9 Available on Serial1{{#if has-serial2}}, Serial2{{/if}}{{#if has-serial4-5}}, Serial4, Serial5{{/if}}._ -_Since 0.5.0. Available on USB Serial (Serial)_ +_Since 0.5.0 Available on USB Serial (Serial)_ + +{{#if has-usb-serial1}}_Since 0.6.0 Available on `USBSerial1`_{{/if}} Retrieves the number of bytes (characters) that can be written to this serial port without blocking. If `blockOnOverrun(false)` has been called, the method returns the number of bytes that can be written to the buffer without causing buffer overrun, which would cause old data to be discarded and overwritten. +{{#if has-usb-serial1}} +Also see [`acquireSerialBuffer`](#acquireserialbuffer-). +{{/if}} + +{{#if has-usb-serial1}} +### acquireSerialBuffer() + +```C++ +// SYNTAX +HAL_USB_USART_Config acquireSerialBuffer() +{ + HAL_USB_USART_Config conf = {0}; + + // The usable buffer size will be 128 + static uint8_t serial_rx_buffer[129]; + static uint8_t serial_tx_buffer[129]; + + conf.rx_buffer = serial_rx_buffer; + conf.tx_buffer = serial_tx_buffer; + conf.rx_buffer_size = 129; + conf.tx_buffer_size = 129; + + return conf; +} + +HAL_USB_USART_Config acquireUSBSerial1Buffer() +{ + HAL_USB_USART_Config conf = {0}; + + // The usable buffer size will be 128 + static uint8_t usbserial1_rx_buffer[129]; + static uint8_t usbserial1_tx_buffer[129]; + + conf.rx_buffer = usbserial1_rx_buffer; + conf.tx_buffer = usbserial1_tx_buffer; + conf.rx_buffer_size = 129; + conf.tx_buffer_size = 129; + + return conf; +} +``` + +_Since 0.6.0_ + +It is possible for the application to allocate its own buffers for `Serial` and `USBSerial1` by implementing `acquireSerialBuffer` and `acquireUSBSerial1Buffer` functions. Minimum receive buffer size is 65 bytes. + +{{/if}} {{!-- has-usb-serial1 --}} + ### blockOnOverrun() -_Since 0.4.9. Available on Serial1, Serial2, etc._ +_Since 0.4.9 Available on Serial1{{#if has-serial2}}, Serial2{{/if}}{{#if has-serial4-5}}, Serial4, Serial5{{/if}}._ -_Since 0.5.0. Available on USB Serial (Serial)_ +_Since 0.5.0 Available on USB Serial (Serial)_ + +{{#if has-usb-serial1}}_Since 0.6.0 Available on `USBSerial1`_{{/if}} Defines what should happen when calls to `write()/print()/println()/printlnf()` that would overrun the buffer. @@ -3090,14 +3602,17 @@ A family of application-defined functions that are called whenever there is data from a serial peripheral. - serialEvent: called when there is data available from `Serial` +{{#if has-usb-serial1}}- usbSerialEvent1: called when there is data available from `USBSerial1`{{/if}} - serialEvent1: called when there is data available from `Serial1` +{{#if has-serial2}} - serialEvent2: called when there is data available from `Serial2` -{{#if electron}} +{{/if}} {{!-- has-serial2 --}} +{{#if has-serial4-5}} - serialEvent4: called when there is data available from `Serial4` - serialEvent5: called when there is data available from `Serial5` -{{/if}} +{{/if}} {{!-- has-serial4-5 --}} -The `serialEvent` functions are called by the system as part of the application loop. Since these is an +The `serialEvent` functions are called by the system as part of the application loop. Since these are an extension of the application loop, it is ok to call any functions at you would also call from loop(). ```cpp @@ -3118,6 +3633,8 @@ void serialEvent() ### peek() +_Available on Serial, {{#if has-usb-serial1}}USBSerial1, {{/if}}Serial1{{#if has-serial2}}, Serial2{{/if}}{{#if has-serial4-5}}, Serial4, Serial5{{/if}}._ + Returns the next byte (character) of incoming serial data without removing it from the internal serial buffer. That is, successive calls to peek() will return the same character, as will the next call to `read()`. ```C++ @@ -3129,6 +3646,8 @@ Serial1.peek(); ### write() +_Available on Serial, Serial1{{#if has-serial2}}, Serial2{{/if}}{{#if has-serial4-5}}, Serial4, Serial5{{/if}}._ + Writes binary data to the serial port. This data is sent as a byte or series of bytes; to send the characters representing the digits of a number use the `print()` function instead. ```C++ @@ -3166,6 +3685,8 @@ void loop() ### read() +_Available on Serial, {{#if has-usb-serial1}}USBSerial1, {{/if}}Serial1{{#if has-serial2}}, Serial2{{/if}}{{#if has-serial4-5}}, Serial4, Serial5{{/if}}._ + Reads incoming serial data. ```C++ @@ -3197,6 +3718,8 @@ void loop() { ``` ### print() +_Available on Serial, {{#if has-usb-serial1}}USBSerial1, {{/if}}Serial1{{#if has-serial2}}, Serial2{{/if}}{{#if has-serial4-5}}, Serial4, Serial5{{/if}}._ + Prints data to the serial port as human-readable ASCII text. This command can take many forms. Numbers are printed using an ASCII character for each digit. Floats are similarly printed as ASCII digits, defaulting to two decimal places. Bytes are sent as a single character. Characters and strings are sent as is. For example: @@ -3217,6 +3740,8 @@ An optional second parameter specifies the base (format) to use; permitted value ### println() +_Available on Serial, {{#if has-usb-serial1}}USBSerial1, {{/if}}Serial1{{#if has-serial2}}, Serial2{{/if}}{{#if has-serial4-5}}, Serial4, Serial5{{/if}}._ + Prints data to the serial port as human-readable ASCII text followed by a carriage return character (ASCII 13, or '\r') and a newline character (ASCII 10, or '\n'). This command takes the same forms as `Serial.print()`. ```C++ @@ -3264,7 +3789,9 @@ void loop() { ### printf() -*Since 0.4.6.* +_Since 0.4.6_ + +_Available on Serial, {{#if has-usb-serial1}}USBSerial1, {{/if}}Serial1{{#if has-serial2}}, Serial2{{/if}}{{#if has-serial4-5}}, Serial4, Serial5{{/if}}._ Provides [printf](http://www.cplusplus.com/reference/cstdio/printf/)-style formatting over serial. @@ -3288,7 +3815,9 @@ The last `printf()` call could be changed to `printlnf()` to avoid a separate ca ### printlnf() -*Since 0.4.6.* +_Since 0.4.6_ + +_Available on Serial, {{#if has-usb-serial1}}USBSerial1, {{/if}}Serial1{{#if has-serial2}}, Serial2{{/if}}{{#if has-serial4-5}}, Serial4, Serial5{{/if}}._ formatted output followed by a newline. Produces the same output as [printf](#printf-) which is then followed by a newline character, @@ -3299,20 +3828,18 @@ so to that subsequent output appears on the next line. Waits for the transmission of outgoing serial data to complete. -**NOTE:** That this function does nothing at present, in particular it doesn't -wait for the data to be sent, since this causes the application to wait indefinitely -when there is no serial monitor connected. - ```C++ // SYNTAX Serial.flush(); Serial1.flush(); ``` -`flush()` neither takes a parameter nor returns anything +`flush()` neither takes a parameter nor returns anything. ### halfduplex() +_Available on Serial1{{#if has-serial2}}, Serial2{{/if}}{{#if has-serial4-5}}, Serial4, Serial5{{/if}}._ + Puts Serial1 into half-duplex mode. In this mode both the transmit and receive are on the TX pin. This mode can be used for a single wire bus communications scheme between microcontrollers. @@ -3335,4895 +3862,6397 @@ Serial1.halfduplex(true); `halfduplex()` returns nothing +### isConnected() +```C++ +// EXAMPLE USAGE +void setup() +{ + Serial.begin(); // open serial over USB + while(!Serial.isConnected()) // wait for Host to open serial port + Particle.process(); -SPI ----- -This library allows you to communicate with SPI devices, with the {{device}} as the master device. + Serial.println("Hello there!"); +} +``` -{{#unless core}} -_Since 0.5.0_ the {{device}} can function as a slave. -{{/unless}} +_Since 0.5.3 Available on `Serial`._ -{{#if core}} -![SPI](/assets/images/core-pin-spi.jpg) -{{/if}} +_Since 0.6.0 Available on `Serial`{{#if has-usb-serial1}} and `USBSerial1`{{/if}}._ -The hardware SPI pin functions, which can -be used via the `SPI` object, are mapped as follows: -* `SS` => `A2` (default) -* `SCK` => `A3` -* `MISO` => `A4` -* `MOSI` => `A5` +Used to check if host has serial port (virtual COM port) open. -{{#unless core}} -There is a second hardware SPI interface available, which can -be used via the `SPI1` object. This second port is mapped as follows: -* `SS` => `D5` (default) -* `SCK` => `D4` -* `MISO` => `D3` -* `MOSI` => `D2` -{{/unless}} +{{#if core}}***NOTE:*** This function always returns `true` on {{device}}.{{/if}} -{{#if electron}} -Additionally on the Electron, there is an alternate pin location for the second SPI interface, which can -be used via the `SPI2` object. This alternate location is mapped as follows: -* `SS` => `D5` (default) -* `SCK` => `C3` -* `MISO` => `C2` -* `MOSI` => `C1` -{{/if}} +Returns: +- `true` when Host has virtual COM port open. -{{#unless core}} -**Note**: Because there are multiple SPI peripherals available, be sure to use the same `SPI`,`SPI1`{{#if electron}},`SPI2`{{/if}} object with all associated functions. I.e., +{{#if has-usb-hid}} +Mouse +---- -Do **NOT** use **SPI**.begin() with **SPI1**.transfer(); +```cpp +// EXAMPLE USAGE +// Use STARTUP() macro to avoid USB disconnect/reconnect (see begin() documentation) +STARTUP(Mouse.begin()); -**Do** use **SPI**.begin() with **SPI**.transfer(); -{{/unless}} +void setup() { + // Set screen size to 1920x1080 (to scale [0, 32767] absolute Mouse coordinates) + Mouse.screenSize(1920, 1080); + // Move mouse to the center of the screen and click left button + Mouse.moveTo(1920 / 2, 1080 / 2); + Mouse.click(MOUSE_LEFT); + // Move mouse from the current position by 100 points (not pixels) left + Mouse.move(-100, 0); + // Press right mouse button (and leave it pressed) + Mouse.press(MOUSE_RIGHT); + // Scroll wheel in the negative direction + Mouse.scroll(-127); + // Release right mouse button + Mouse.release(MOUSE_RIGHT); +} -### begin() +void loop() { +} +``` -Initializes the SPI bus by setting SCK, MOSI, and a user-specified slave-select pin to outputs, MISO to input. SCK is pulled either high or low depending on the configured SPI data mode (default high for `SPI_MODE3`). Slave-select is pulled high. +_Since 0.6.0_ -**Note:** The SPI firmware ONLY initializes the user-specified slave-select pin as an `OUTPUT`. The user's code must control the slave-select pin with `digitalWrite()` before and after each SPI transfer for the desired SPI slave device. Calling `SPI.end()` does NOT reset the pin mode of the SPI pins. +This library allows {{device}} to act as a native USB HID Mouse. -```C++ +In terms of USB HID, {{device}} presents itself as two separate devices: Mouse (supporting relative movement) and Digitizer (supporting absolute movement). + +Full capabilities include: +- Relative XY movement [-32767, 32767] +- Absolute XY movement [0, 32767] +- 3-buttons (left, right, middle) +- Wheel [-127, 127] + +***NOTE:*** Linux X11 doesn't support HID devices reporting both absolute and relative coordinates. By default only absolute movement is possible by using [`Mouse.moveTo()`](#moveto-). In order for regular relative [`Mouse.move()`](#move-) to work, a call to [`Mouse.enableMoveTo(false)`](#enablemoveto-) is required. + +### begin() + +```cpp // SYNTAX -SPI.begin(ss); -{{#unless core}} -SPI1.begin(ss); -{{/unless}} -{{#if electron}} -SPI2.begin(ss); -{{/if}} +Mouse.begin(); ``` -Where, the parameter `ss` is the `SPI` device slave-select pin to initialize. If no pin is specified, the default pin is `SS (A2)`. -{{#unless core}} -For `SPI1`, the default `ss` pin is `SS (D5)`. -{{/unless}} -{{#if electron}} -For `SPI2`, the default `ss` pin is also `SS (D5)`. -{{/if}} +Initializes Mouse library and enables USB HID stack. -{{#unless core}} -```C++ -// Example using SPI1, with D5 as the SS pin: -SPI1.begin(); -// or -SPI1.begin(D5); -``` -{{/unless}} -{{#if electron}} -```C++ -// Example using SPI2, with C0 as the SS pin: -SPI2.begin(C0); +```cpp +// Example +STARTUP(Mouse.begin()); +void setup() { + // At this point {{device}} is already connected to Host with Mouse enabled +} ``` -{{/if}} -{{#unless core}} - -### begin(SPI_Mode, uint16_t) +***NOTE:*** When `Mouse.begin()` is called in `setup()` or during normal application execution, the device will quickly disconnect from Host and connect back with USB HID enabled. If such behavior is undesirable, `Mouse` may be enabled with `STARTUP()` macro, which will force the device to connect to the Host after booting with `Mouse` already enabled. -_Since 0.5.0_ +This function takes no parameters and does not return anything. -Initializes the {{device}} SPI peripheral in master or slave mode. +### end() -**Note:** MISO, MOSI and SCK idle in high-impedance state when SPI peripheral is configured in slave mode and the device is not selected. +```cpp +// SYNTAX +Mouse.end(); +``` -Parameters: +Disables USB Mouse functionality. -- `mode`: `SPI_MODE_MASTER` or `SPI_MODE_SLAVE` -- `ss_pin`: slave-select pin to initialize. If no pin is specified, the default pin is `SS (A2)`. For `SPI1`, the default pin is `SS (D5)`. {{#if electron}}For `SPI2`, the default pin is also `SS (D5)`.{{/if}} +```cpp +// Example +// Enable both Keyboard and Mouse on startup +STARTUP(Mouse.begin()); +STARTUP(Keyboard.begin()); -```C++ -// Example using SPI in master mode, with A2 (default) as the SS pin: -SPI.begin(SPI_MODE_MASTER); -// Example using SPI1 in slave mode, with D5 as the SS pin -SPI1.begin(SPI_MODE_SLAVE, D5); -// Example using SPI2 in slave mode, with C0 as the SS pin -SPI2.begin(SPI_MODE_SLAVE, C0); +void setup() { + // A call to Mouse.end() here will not cause the device to disconnect and connect back to the Host + Mouse.end(); + // Disabling both Keyboard and Mouse at this point will trigger the behavior explained in NOTE. + Keyboard.end(); +} ``` -{{/unless}} +***NOTE:*** Calling `Mouse.end()` will cause the device to quickly disconnect from Host and connect back without USB HID enabled if [`Keyboard`](#keyboard) is disabled as well. -### end() +This function takes no parameters and does not return anything. -Disables the SPI bus (leaving pin modes unchanged). +### move() -```C++ +```cpp // SYNTAX -SPI.end(); -{{#unless core}} -SPI1.end(); -{{/unless}} -{{#if electron}} -SPI2.end(); -{{/if}} +Mouse.move(x, y); +Mouse.move(x, y, wheel); ``` -### setBitOrder() +Moves the cursor relative to the current position. -Sets the order of the bits shifted out of and into the SPI bus, either LSBFIRST (least-significant bit first) or MSBFIRST (most-significant bit first). +*Parameters:* -```C++ +- `x`: amount to move along the X axis - `int16_t` [-32767, 32767] +- `y`: amount to move along the Y axis - `int16_t` [-32767, 32767] +- `wheel`: amount to move the scroll wheel - `int8_t` [-127, 127] + +`move()` does not return anything. + +### moveTo() + +```cpp // SYNTAX -SPI.setBitOrder(order); -{{#unless core}} -SPI1.setBitOrder(order); -{{/unless}} -{{#if electron}} -SPI2.setBitOrder(order); -{{/if}} +Mouse.moveTo(x, y); ``` -Where, the parameter `order` can either be `LSBFIRST` or `MSBFIRST`. +Moves the cursor to an absolute position. (0, 0) position is the top left corner of the screen. By default both X and Y axes span from 0 to 32767. -### setClockSpeed +The default range [0, 32767] can be mapped to actual screen resolution by calling [`screenSize()`](#screensize-). After the call to [`screenSize()`](#screensize-), `moveTo()` will accept screen coordinates and automatically map them to the default range. -Sets the SPI clock speed. The value can be specified as a direct value, or as -as a value plus a multiplier. +*Parameters:* +- `x`: X coordinate - `uint16_t` _[0, 32767] (default)_ +- `y`: Y coordinate - `uint16_t` _[0, 32767] (default)_ -```C++ +`moveTo()` does not return anything. + +### scroll() + +```cpp // SYNTAX -SPI.setClockSpeed(value, scale); -SPI.setClockSpeed(frequency); -{{#unless core}} -SPI1.setClockSpeed(value, scale); -SPI1.setClockSpeed(frequency); -{{/unless}} -{{#if electron}} -SPI2.setClockSpeed(value, scale); -SPI2.setClockSpeed(frequency); -{{/if}} +Mouse.scroll(wheel); ``` -``` -// EXAMPLE -// Set the clock speed as close to 15MHz (but not over) -SPI.setClockSpeed(15, MHZ); -SPI.setClockSpeed(15000000); -``` +Scrolls the mouse wheel by the specified amount. -The clock speed cannot be set to any arbitrary value, but is set internally by using a -divider (see `SPI.setClockDivider()`) that gives the highest clock speed not greater -than the one specified. +*Parameters:* -This method can make writing portable code easier, since it specifies the clock speed -absolutely, giving comparable results across devices. In contrast, specifying -the clock speed using dividers is typically not portable since is dependent upon the system clock speed. - -### setClockDividerReference +- `wheel`: amount to move the scroll wheel - `int8_t` [-127, 127] -This function aims to ease porting code from other platforms by setting the clock speed that -`SPI.setClockDivider` is relative to. - -For example, when porting an Arduino SPI library, each to `SPI.setClockDivider()` would -need to be changed to reflect the system clock speed of the device being used. +`scroll()` does not return anything. -This can be avoided by placing a call to `SPI.setClockDividerReference()` before the other SPI calls. +### click() ```cpp +// SYNTAX +Mouse.click(); +Mouse.click(button); +``` -// setting divider reference - -// place this early in the library code -SPI.setClockDividerReference(SPI_CLK_ARDUINO); - -// then all following calls to setClockDivider() will give comparable clock speeds -// to running on the Arduino Uno +Momentarily clicks specified mouse button at the current cursor position. A click is a [`press()`](#press-) quickly followed by [`release()`](#release-). -// sets the clock to as close to 4MHz without going over. -SPI.setClockDivider(SPI_CLK_DIV4); +```cpp +// EXAMPLE USAGE +// Click left mouse button +Mouse.click(MOUSE_LEFT); +// Click right mouse button +Mouse.click(MOUSE_RIGHT); +// Click middle mouse button +Mouse.click(MOUSE_MIDDLE); +// Click both left and right mouse buttons at the same time +Mouse.click(MOUSE_LEFT | MOUSE_RIGHT); ``` -The default clock divider reference is the system clock. {{#if core}}On the Core, this is 72 MHz.{{/if}} {{#unless core}}On the Photon and Electron, the system clock speeds are: -- SPI - 60 MHz -- SPI1 - 30 MHz -{{/unless}} +*Parameters:* -### setClockDivider() +- `button`: which mouse button to click - `uint8_t` - `MOUSE_LEFT` (default), `MOUSE_RIGHT`, `MOUSE_MIDDLE` or any ORed (`|`) combination of buttons for simultaneous clicks -Sets the SPI clock divider relative to the selected clock reference. The available dividers are 2, 4, 8, 16, 32, 64, 128 or 256. The default setting is SPI_CLOCK_DIV4, which sets the SPI clock to one-quarter the frequency of the system clock. +`click()` does not return anything. -```C++ +### press() + +```cpp // SYNTAX -SPI.setClockDivider(divider); -{{#unless core}} -SPI1.setClockDivider(divider); -{{/unless}} -{{#if electron}} -SPI2.setClockDivider(divider); -{{/if}} +Mouse.press(); +Mouse.press(button); ``` -Where the parameter, `divider` can be: - - - `SPI_CLOCK_DIV2` - - `SPI_CLOCK_DIV4` - - `SPI_CLOCK_DIV8` - - `SPI_CLOCK_DIV16` - - `SPI_CLOCK_DIV32` - - `SPI_CLOCK_DIV64` - - `SPI_CLOCK_DIV128` - - `SPI_CLOCK_DIV256` - -### setDataMode() -Sets the SPI data mode: that is, clock polarity and phase. See the [Wikipedia article on SPI](http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus) for details. +Presses specified mouse button at the current cursor position and holds it pressed. A press can be cancelled by [`release()`](#release-). -```C++ -// SYNTAX -SPI.setDataMode(mode); -{{#unless core}} -SPI1.setDataMode(mode); -{{/unless}} -{{#if electron}} -SPI2.setDataMode(mode); -{{/if}} +```cpp +// EXAMPLE USAGE +// Press left mouse button +Mouse.press(MOUSE_LEFT); +// Press right mouse button +Mouse.press(MOUSE_RIGHT); +// Press middle mouse button +Mouse.press(MOUSE_MIDDLE); +// Press both left and right mouse buttons at the same time +Mouse.press(MOUSE_LEFT | MOUSE_RIGHT); ``` -Where the parameter, `mode` can be: - - `SPI_MODE0` - - `SPI_MODE1` - - `SPI_MODE2` - - `SPI_MODE3` +*Parameters:* -### transfer() +- `button`: which mouse button to press - `uint8_t` - `MOUSE_LEFT` (default), `MOUSE_RIGHT`, `MOUSE_MIDDLE` or any ORed (`|`) combination of buttons for simultaneous press -Transfers one byte over the SPI bus, both sending and receiving. +`press()` does not return anything. -```C++ +### release() + +```cpp // SYNTAX -SPI.transfer(val); -{{#unless core}} -SPI1.transfer(val); -{{/unless}} -{{#if electron}} -SPI2.transfer(val); -{{/if}} +Mouse.release(); +Mouse.release(button); ``` -Where the parameter `val`, can is the byte to send out over the SPI bus. - -{{#unless core}} -### transfer(void\*, void\*, size_t, std::function) - -For transferring a large number of bytes, this form of transfer() uses DMA to speed up SPI data transfer and at the same time allows you to run code in parallel to the data transmission. The function initialises, configures and enables the DMA peripheral’s channel and stream for the selected SPI peripheral for both outgoing and incoming data and initiates the data transfer. If a user callback function is passed then it will be called after completion of the DMA transfer. This results in asynchronous filling of RX buffer after which the DMA transfer is disabled till the transfer function is called again. If NULL is passed as a callback then the result is synchronous i.e. the function will only return once the DMA transfer is complete. -**Note**: The SPI protocol is based on a one byte OUT / one byte IN inteface. For every byte expected to be received, one (dummy, typically 0x00 or 0xFF) byte must be sent. +Releases previously pressed mouse button at the current cursor position. -```C++ -// SYNTAX -SPI.transfer(tx_buffer, rx_buffer, length, myFunction); -{{#unless core}} -SPI1.transfer(tx_buffer, rx_buffer, length, myFunction); -{{/unless}} -{{#if electron}} -SPI2.transfer(tx_buffer, rx_buffer, length, myFunction); -{{/if}} +```cpp +// EXAMPLE USAGE +// Release left mouse button +Mouse.release(MOUSE_LEFT); +// Release right mouse button +Mouse.release(MOUSE_RIGHT); +// Release middle mouse button +Mouse.release(MOUSE_MIDDLE); +// Release both left and right mouse buttons at the same time +Mouse.release(MOUSE_LEFT | MOUSE_RIGHT); ``` -Parameters: - -- `tx_buffer`: array of Tx bytes that is filled by the user before starting the SPI transfer. If `NULL`, default dummy 0xFF bytes will be clocked out. -- `rx_buffer`: array of Rx bytes that will be filled by the slave during the SPI transfer. If `NULL`, the received data will be discarded. -- `length`: number of data bytes that are to be transferred -- `myFunction`: user specified function callback to be called after completion of the SPI DMA transfer +*Parameters:* -NOTE: `tx_buffer` and `rx_buffer` sizes MUST be identical (of size `length`) +- `button`: which mouse button to release - `uint8_t` - `MOUSE_LEFT` (default), `MOUSE_RIGHT`, `MOUSE_MIDDLE` or any ORed (`|`) combination of buttons to release simultaneously. To release all buttons simultaneously, `MOUSE_ALL` can also be used. -_Since 0.5.0_ When SPI peripheral is configured in slave mode, the transfer will be canceled when the master deselects this slave device. The user application can check the actual number of bytes received/transmitted by calling `available()`. +`release()` does not return anything. -### transferCancel() +### isPressed() -_Since 0.5.0_ +```cpp +// SYNTAX +Mouse.isPressed(); +Mouse.isPressed(button); +``` -Aborts the configured DMA transfer and disables the DMA peripheral’s channel and stream for the selected SPI peripheral for both outgoing and incoming data. +This function checks the current state of mouse buttons and returns if they are currently pressed or not. -**Note**: The user specified SPI DMA transfer completion function will still be called to indicate the end of DMA transfer. The user application can check the actual number of bytes received/transmitted by calling `available()`. +```cpp +// EXAMPLE USAGE +bool pressed; +// Check if left mouse button is currently pressed +pressed = Mouse.isPressed(MOUSE_LEFT); +// Check if right mouse button is currently pressed +pressed = Mouse.isPressed(MOUSE_RIGHT); +// Check if middle mouse button is currently pressed +pressed = Mouse.isPressed(MOUSE_MIDDLE); +``` -### onSelect() +*Parameters:* -_Since 0.5.0_ +- `button`: which mouse button to check - `uint8_t` - `MOUSE_LEFT` (default), `MOUSE_RIGHT`, `MOUSE_MIDDLE` -Registers a function to be called when the SPI master selects or deselects this slave device by pulling configured slave-select pin low (selected) or high (deselected). +`isPressed()` returns `true` if provided button is currently pressed. -Parameters: `handler`: the function to be called when the slave is selected or deselected; this should take a single uint8_t parameter (the current state: `1` - selected, `0` - deselected) and return nothing, e.g.: `void myHandler(uint8_t state)` +### screenSize() -```C++ -// SPI slave example -static uint8_t rx_buffer[64]; -static uint8_t tx_buffer[64]; -static uint32_t select_state = 0x00; -static uint32_t transfer_state = 0x00; +```cpp +// SYNTAX +Mouse.screenSize(screenWidth, screenHeight); +Mouse.screenSize(screenWidth, screenHeight, + marginLeft, marginRight, + marginTop, marginBottom); +Mouse.screenSize(screenWidth, screenHeight, + std::array<4, float>); +``` -void onTransferFinished() { - transfer_state = 1; -} +Maps the default absolute movement range [0, 32767] used by [`moveTo()`](#moveto-) to actual screen resolution. After setting the screen size, `moveTo()` will accept screen coordinates and automatically map them to the default range. -void onSelect(uint8_t state) { - if (state) - select_state = state; -} +```cpp +// EXAMPLE USAGE +// Use STARTUP() macro to avoid USB disconnect/reconnect (see begin() documentation) +STARTUP(Mouse.begin()); -/* executes once at startup */ void setup() { - Serial.begin(9600); - for (int i = 0; i < sizeof(tx_buffer); i++) - tx_buffer[i] = (uint8_t)i; - SPI.onSelect(onSelect); - SPI.begin(SPI_MODE_SLAVE, A2); + // Set screen size to 1920x1080 (to scale [0, 32767] absolute Mouse coordinates) + Mouse.screenSize(1920, 1080); + // Move mouse to the center of the screen + Mouse.moveTo(1920 / 2, 1080 / 2); } -/* executes continuously after setup() runs */ void loop() { - while (1) { - while(select_state == 0); - select_state = 0; - - transfer_state = 0; - SPI.transfer(tx_buffer, rx_buffer, sizeof(rx_buffer), onTransferFinished); - while(transfer_state == 0); - if (SPI.available() > 0) { - Serial.printf("Received %d bytes", SPI.available()); - Serial.println(); - for (int i = 0; i < SPI.available(); i++) { - Serial.printf("%02x ", rx_buffer[i]); - } - Serial.println(); - } - } } ``` -### available() +*Parameters:* -_Since 0.5.0_ +- `screenWidth`: screen width in pixels - `uint16_t` +- `screenHeight`: screen height in pixels - `uint16_t` +- `marginLeft`: _(optional)_ left screen margin in percent (e.g. 10.0) - `float` +- `marginRight`: _(optional)_ right screen margin in percent (e.g. 10.0) - `float` +- `marginTop`: _(optional)_ top screen margin in percent (e.g. 10.0) - `float` +- `marginBottom`: _(optional)_ bottom screen margin in percent (e.g. 10.0) - `float` -Returns the number of bytes available for reading in the `rx_buffer` supplied in `transfer()`. In general, returns the actual number of bytes received/transmitted during the ongoing or finished DMA transfer. +`screenSize()` does not return anything. -```C++ +### enableMoveTo() + +```cpp // SYNTAX -SPI.available(); +Mouse.enableMoveTo(false); +Mouse.enableMoveTo(true); ``` -Returns the number of bytes available. +Disables or enables absolute mouse movement (USB HID Digitizer). -{{/unless}} +```cpp +// EXAMPLE USAGE +// Use STARTUP() macro to avoid USB disconnect/reconnect (see begin() documentation) +STARTUP(Mouse.begin()); +// Disable absolute mouse movement +STARTUP(Mouse.enableMoveTo(false)); -Wire (I2C) ----- +void setup() { + // Move cursor by 100 points along X axis and by 100 points Y axis + Mouse.move(100, 100); + // Mouse.moveTo() calls do nothing + Mouse.moveTo(0, 0); +} -{{#unless electron}} -![I2C](/assets/images/core-pin-i2c.jpg) -{{/unless}} +void loop() { +} +``` -This library allows you to communicate with I2C / TWI(Two Wire Interface) devices. On the Core/Photon/Electron, D0 is the Serial Data Line (SDA) and D1 is the Serial Clock (SCL). {{#if electron}}Additionally on the Electron, there is an alternate pin location for the I2C interface: C4 is the Serial Data Line (SDA) and C5 is the Serial Clock (SCL).{{/if}} Both SCL and SDA pins are open-drain outputs that only pull LOW and typically operate with 3.3V logic, but are tolerant to 5V. Connect a pull-up resistor(1.5k to 10k) on the SDA line to 3V3. Connect a pull-up resistor(1.5k to 10k) on the SCL line to 3V3. If you are using a breakout board with an I2C peripheral, check to see if it already incorporates pull-up resistors. +***NOTE:*** Linux X11 doesn't support HID devices reporting both absolute and relative coordinates. By default only absolute movement is possible by using [`Mouse.moveTo()`](#moveto-). In order for regular relative [`Mouse.move()`](#move-) to work, a call to [`Mouse.enableMoveTo(false)`](#enablemoveto-) is required. -These pins are used via the `Wire` object. +***NOTE:*** When `Mouse.enableMoveTo()` is called in `setup()` or during normal application execution, the device will quickly disconnect from Host and connect back with new settings. If such behavior is undesirable, `moveTo()` may be disable or enabled with `STARTUP()` macro, which will force the device to connect to the Host after booting with correct settings already in effect. -* `SCL` => `D1` -* `SDA` => `D0` +*Parameters:* -{{#if electron}} -Additionally on the Electron, there is an alternate pin location for the I2C interface, which can -be used via the `Wire1` object. This alternate location is mapped as follows: -* `SCL` => `C5` -* `SDA` => `C4` +- `state`: `true` to enable absolute movement functionality, `false` to disable - `bool` -**Note**: Because there are multiple I2C locations available, be sure to use the same `Wire` or `Wire1` object with all associated functions. I.e., +`enableMoveTo()` does not return anything. -Do **NOT** use **Wire**.begin() with **Wire1**.write(); +Keyboard +---- -**Do** use **Wire1**.begin() with **Wire1**.transfer(); -{{/if}} +```cpp +// EXAMPLE USAGE +// Use STARTUP() macro to avoid USB disconnect/reconnect (see begin() documentation) +STARTUP(Keyboard.begin()); -### setSpeed() +void setup() { + // Type 'SHIFT+h', 'e', 'l', 'l', 'o', 'SPACE', 'w', 'o', 'r', 'l', 'd', 'ENTER' + Keyboard.println("Hello world!"); -Sets the I2C clock speed. This is an optional call (not from the original Arduino specs.) and must be called once before calling begin(). The default I2C clock speed is 100KHz and the maximum clock speed is 400KHz. + // Type 'SHIFT+t', 'e', 's', 't', 'SPACE', '1', '2', '3', '.', '4', '0', 'ENTER' + Keyboard.printf("%s %.2f\n", "Test", 123.4f); -```C++ -// SYNTAX -Wire.setSpeed(clockSpeed); -Wire.begin(); -``` + // Quickly press and release Ctrl-Alt-Delete + Keyboard.click(KEY_DELETE, MOD_LCTRL | MOD_LALT); -Parameters: + // Press Ctrl, then Alt, then Delete and release them all + Keyboard.press(KEY_LCTRL); + Keyboard.press(KEY_LALT); + Keyboard.press(KEY_DELETE); + Keyboard.releaseAll(); +} -- `clockSpeed`: CLOCK_SPEED_100KHZ, CLOCK_SPEED_400KHZ or a user specified speed in hertz (e.g. `Wire.setSpeed(20000)` for 20kHz) +void loop() { +} +``` -### stretchClock() +_Since 0.6.0_ -Enables or Disables I2C clock stretching. This is an optional call (not from the original Arduino specs.) and must be called once before calling begin(). The default I2C stretch mode is disabled. +This library allows {{device}} to act as a native USB HID Keyboard. -```C++ +### begin() + +```cpp // SYNTAX -Wire.stretchClock(stretch); -Wire.begin(); +Keyboard.begin(); ``` -Parameters: +Initializes Keyboard library and enables USB HID stack. -- `stretch`: boolean. `true` will enable clock stretching. `false` will disable clock stretching. +```cpp +// Example +STARTUP(Keyboard.begin()); +void setup() { + // At this point {{device}} is already connected to Host with Mouse enabled +} +``` +***NOTE:*** When `Keyboard.begin()` is called in `setup()` or during normal application execution, the device will quickly disconnect from Host and connect back with USB HID enabled. If such behavior is undesireable, `Keyboard` may be enabled with `STARTUP()` macro, which will force the device to connect to the Host after booting with `Keyboard` already enabled. -### begin() +This function takes no parameters and does not return anything. -Initiate the Wire library and join the I2C bus as a master or slave. This should normally be called only once. +### end() -```C++ +```cpp // SYNTAX -Wire.begin(); -Wire.begin(address); +Keyboard.end(); ``` -Parameters: `address`: the 7-bit slave address (optional); if not specified, join the bus as an I2C master. If address is specified, join the bus as an I2C slave. - +Disables USB Keyboard functionality. -### end() +```cpp +// Example +// Enable both Keyboard and Mouse on startup +STARTUP(Mouse.begin()); +STARTUP(Keyboard.begin()); -*Since 0.4.6.* +void setup() { + // A call to Mouse.end() here will not cause the device to disconnect and connect back to the Host + Mouse.end(); + // Disabling both Keyboard and Mouse at this point will trigger the behavior explained in NOTE. + Keyboard.end(); +} +``` -Releases the I2C bus so that the pins used by the I2C bus are available for general purpose I/O. +***NOTE:*** Calling `Keyboard.end()` will cause the device to quickly disconnect from Host and connect back without USB HID enabled if [`Mouse`](#mouse) is disabled as well. -### isEnabled() +This function takes no parameters and does not return anything. -Used to check if the Wire library is enabled already. Useful if using multiple slave devices on the same I2C bus. Check if enabled before calling Wire.begin() again. +### write() -```C++ +```cpp // SYNTAX -Wire.isEnabled(); +Keyboard.write(character); ``` -Returns: boolean `true` if I2C enabled, `false` if I2C disabled. +Momementarily clicks a keyboard key. A click is a [`press()`](#press--1) quickly followed by [`release()`](#release--1). This function works only with ASCII characters. ASCII characters are translated into USB HID keycodes according to the [conversion table](https://github.com/spark/firmware/blob/develop/wiring/src/spark_wiring_usbkeyboard.cpp#L33). For example ASCII character 'a' would be translated into 'a' keycode (leftmost middle row letter key on a QWERTY keyboard), whereas 'A' ASCII character would be sent as 'a' keycode with SHIFT modifier. -```C++ +```cpp // EXAMPLE USAGE +STARTUP(Keyboard.begin()); -// Initialize the I2C bus if not already enabled -if ( !Wire.isEnabled() ) { - Wire.begin(); +void setup() { + const char hello[] = "Hello world!\n"; + // This for-loop will type "Hello world!" followed by ENTER + for (int i = 0; i < strlen(hello); i++) { + Keyboard.write(hello[i]); + } } ``` -### requestFrom() +This function is used by [`print()`](#print--1), [`println()`](#println--1), [`printf()`](#printf--1), [`printlnf()`](#printlnf--1) which provide an easy way to type text. -Used by the master to request bytes from a slave device. The bytes may then be retrieved with the `available()` and `read()` functions. +*Parameters:* -```C++ -// SYNTAX -Wire.requestFrom(address, quantity); -Wire.requestFrom(address, quantity, stop) ; -``` - -Parameters: - -- `address`: the 7-bit address of the device to request bytes from -- `quantity`: the number of bytes to request (Max. 32) -- `stop`: boolean. `true` will send a stop message after the request, releasing the bus. `false` will continually send a restart after the request, keeping the connection active. The bus will not be released, which prevents another master device from transmitting between messages. This allows one master device to send multiple transmissions while in control. If no argument is specified, the default value is `true`. - -Returns: `byte` : the number of bytes returned from the slave device. If a timeout occurs, will return `0`. - -### reset() - -*Since 0.4.6.* - -Attempts to reset the I2C bus. This should be called only if the I2C bus has -has hung. In 0.4.6 additional rework was done for the I2C bus on the Photon and Electron, so -we hope this function isn't required, and it's provided for completeness. +- `ch`: ASCII character - `char` -### beginTransmission() +`write()` does not return anything. -Begin a transmission to the I2C slave device with the given address. Subsequently, queue bytes for transmission with the `write()` function and transmit them by calling `endTransmission()`. +### click() -```C++ +```cpp // SYNTAX -Wire.beginTransmission(address); +Keyboard.click(key); +Keyboard.click(key, modifiers); ``` -Parameters: `address`: the 7-bit address of the device to transmit to. - -### endTransmission() - -Ends a transmission to a slave device that was begun by `beginTransmission()` and transmits the bytes that were queued by `write()`. +Momementarily clicks a keyboard key as well as one or more modifier keys (e.g. ALT, CTRL, SHIFT etc.). A click is a [`press()`](#press--1) quickly followed by [`release()`](#release--1). This function works only with USB HID [keycodes (defined in `enum UsbKeyboardScanCode`)](https://github.com/spark/firmware/blob/develop/wiring/inc/spark_wiring_usbkeyboard_scancode.h#L5) and [modifiers (defined in `enum UsbKeyboardModifier`)](https://github.com/spark/firmware/blob/develop/wiring/inc/spark_wiring_usbkeyboard_scancode.h#L396). `Keyboard` implementation supports keycodes ranging from `0x04 (KEY_A / Keyboard a and A)` to `0xDD (KEY_KPHEX / Keypad Hexadecimal)`. +```cpp +// EXAMPLE USAGE +STARTUP(Keyboard.begin()); -```C++ -// SYNTAX -Wire.endTransmission(); -Wire.endTransmission(stop); +void setup() { + // Quickly press and release Ctrl-Alt-Delete + Keyboard.click(KEY_DELETE, MOD_LCTRL | MOD_LALT); +} ``` -Parameters: `stop` : boolean. -`true` will send a stop message after the last byte, releasing the bus after transmission. `false` will send a restart, keeping the connection active. The bus will not be released, which prevents another master device from transmitting between messages. This allows one master device to send multiple transmissions while in control. If no argument is specified, the default value is `true`. - -Returns: `byte`, which indicates the status of the transmission: +*Parameters:* -- 0: success -- 1: busy timeout upon entering endTransmission() -- 2: START bit generation timeout -- 3: end of address transmission timeout -- 4: data byte transfer timeout -- 5: data byte transfer succeeded, busy timeout immediately after +- `key`: USB HID key code (see [`enum UsbKeyboardScanCode`](https://github.com/spark/firmware/blob/develop/wiring/inc/spark_wiring_usbkeyboard_scancode.h#L5)) - `uint16_t` +- `modifier`: _(optional)_ one or more ORed (`|`) USB HID modifier codes (see [`enum UsbKeyboardModifier`](https://github.com/spark/firmware/blob/develop/wiring/inc/spark_wiring_usbkeyboard_scancode.h#L396) - `uint16_t` -### write() +`click()` does not return anything. -Writes data from a slave device in response to a request from a master, or queues bytes for transmission from a master to slave device (in-between calls to `beginTransmission()` and `endTransmission()`). Buffer size is truncated to 32 bytes; writing bytes beyond 32 before calling endTransmission() will be ignored. +### press() -```C++ +```cpp // SYNTAX -Wire.write(value); -Wire.write(string); -Wire.write(data, length); +Keyboard.press(key); +Keyboard.press(key, modifier); ``` -Parameters: -- `value`: a value to send as a single byte -- `string`: a string to send as a series of bytes -- `data`: an array of data to send as bytes -- `length`: the number of bytes to transmit (Max. 32) +Presses specified keyboard key as well as one or more modifier keys and holds them pressed. A press can be cancelled by [`release()`](#release--1) or [`releaseAll()`](#releaseall-). -Returns: `byte` +Up to 8 keys can be pressed simultaneously. Modifier keys (e.g. CTRL, ALT, SHIFT etc) are sent separately and do not add to the currently pressed key count, i.e. it is possible to press and keep pressing 8 regular keyboard keys and all the modifiers (LSHIFT, LALT, LGUI, LCTRL, RSHIFT, RALT, RSHIFT, RCTRL) at the same time. -`write()` will return the number of bytes written, though reading that number is optional. +See [`Keyboard.click()`](#click--1) documentation for information about keycodes and modifier keys. -```C++ +```cpp // EXAMPLE USAGE +STARTUP(Keyboard.begin()); -// Master Writer running on Device No.1 (Use with corresponding Slave Reader running on Device No.2) - -void setup() -{ - Wire.begin(); // join i2c bus as master -} - -byte x = 0; - -void loop() -{ - Wire.beginTransmission(4); // transmit to slave device #4 - Wire.write("x is "); // sends five bytes - Wire.write(x); // sends one byte - Wire.endTransmission(); // stop transmitting - - x++; - delay(500); +void setup() { + // Press Ctrl, then Alt, then Delete and release them all + Keyboard.press(KEY_LCTRL); + Keyboard.press(KEY_LALT); + Keyboard.press(KEY_DELETE); + Keyboard.releaseAll(); } ``` -### available() - -Returns the number of bytes available for retrieval with `read()`. This should be called on a master device after a call to `requestFrom()` or on a slave inside the `onReceive()` handler. - -```C++ -Wire.available(); -``` +*Parameters:* -Returns: The number of bytes available for reading. +- `key`: USB HID key code (see [`enum UsbKeyboardScanCode`](https://github.com/spark/firmware/blob/develop/wiring/inc/spark_wiring_usbkeyboard_scancode.h#L5)) - `uint16_t` +- `modifier`: _(optional)_ one or more ORed (`|`) USB HID modifier codes (see [`enum UsbKeyboardModifier`](https://github.com/spark/firmware/blob/develop/wiring/inc/spark_wiring_usbkeyboard_scancode.h#L396) - `uint16_t` -### read() +`press()` does not return anything. -Reads a byte that was transmitted from a slave device to a master after a call to `requestFrom()` or was transmitted from a master to a slave. `read()` inherits from the `Stream` utility class. +### release() -```C++ +```cpp // SYNTAX -Wire.read() ; +Keyboard.release(key); +Keyboard.release(key, modifier); ``` -Returns: The next byte received +Releases previously pressed keyboard key as well as one or more modifier keys. -```C++ +```cpp // EXAMPLE USAGE +STARTUP(Keyboard.begin()); -// Master Reader running on Device No.1 (Use with corresponding Slave Writer running on Device No.2) - -void setup() -{ - Wire.begin(); // join i2c bus as master - Serial.begin(9600); // start serial for output +void setup() { + // Press Delete and two modifiers (left ALT and left CTRL) simultaneously + Keyboard.press(KEY_DELETE, MOD_LCTRL | MOD_LALT); + // Release Delete and two modifiers (left ALT and left CTRL) simultaneously + Keyboard.release(KEY_DELETE, MOD_LCTRL | MOD_LALT); } +``` -void loop() -{ - Wire.requestFrom(2, 6); // request 6 bytes from slave device #2 +See [`Keyboard.click()`](#click--1) documentation for information about keycodes and modifier keys. - while(Wire.available()) // slave may send less than requested - { - char c = Wire.read(); // receive a byte as character - Serial.print(c); // print the character - } +*Parameters:* - delay(500); -} -``` +- `key`: USB HID key code (see [`enum UsbKeyboardScanCode`](https://github.com/spark/firmware/blob/develop/wiring/inc/spark_wiring_usbkeyboard_scancode.h#L5)) - `uint16_t` +- `modifier`: _(optional)_ one or more ORed (`|`) USB HID modifier codes (see [`enum UsbKeyboardModifier`](https://github.com/spark/firmware/blob/develop/wiring/inc/spark_wiring_usbkeyboard_scancode.h#L396) - `uint16_t` -### peek() +`release()` does not return anything. -Similar in use to read(). Reads (but does not remove from the buffer) a byte that was transmitted from a slave device to a master after a call to `requestFrom()` or was transmitted from a master to a slave. `read()` inherits from the `Stream` utility class. Useful for peeking at the next byte to be read. +### releaseAll() -```C++ +```cpp // SYNTAX -Wire.peek(); +Keyboard.releaseAll(); ``` -Returns: The next byte received (without removing it from the buffer) - -### onReceive() - -Registers a function to be called when a slave device receives a transmission from a master. - -Parameters: `handler`: the function to be called when the slave receives data; this should take a single int parameter (the number of bytes read from the master) and return nothing, e.g.: `void myHandler(int numBytes) ` +Releases any previously pressed keyboard keys and modifier keys. -```C++ +```cpp // EXAMPLE USAGE +STARTUP(Keyboard.begin()); -// Slave Reader running on Device No.2 (Use with corresponding Master Writer running on Device No.1) - -// function that executes whenever data is received from master -// this function is registered as an event, see setup() -void receiveEvent(int howMany) -{ - while(1 < Wire.available()) // loop through all but the last - { - char c = Wire.read(); // receive byte as a character - Serial.print(c); // print the character - } - int x = Wire.read(); // receive byte as an integer - Serial.println(x); // print the integer -} - -void setup() -{ - Wire.begin(4); // join i2c bus with address #4 - Wire.onReceive(receiveEvent); // register event - Serial.begin(9600); // start serial for output -} - -void loop() -{ - delay(100); +void setup() { + // Press Ctrl, then Alt, then Delete and release them all + Keyboard.press(KEY_LCTRL); + Keyboard.press(KEY_LALT); + Keyboard.press(KEY_DELETE); + Keyboard.releaseAll(); } ``` -### onRequest() +This function takes no parameters and does not return anything. -Register a function to be called when a master requests data from this slave device. +### print() -Parameters: `handler`: the function to be called, takes no parameters and returns nothing, e.g.: `void myHandler() ` +See [`Keyboard.write()`](#write--1) and [`Serial.print()`](#print-) documentation. -```C++ -// EXAMPLE USAGE +### println() -// Slave Writer running on Device No.2 (Use with corresponding Master Reader running on Device No.1) +See [`Keyboard.write()`](#write--1) and [`Serial.println()`](#println-) documentation. -// function that executes whenever data is requested by master -// this function is registered as an event, see setup() -void requestEvent() -{ - Wire.write("hello "); // respond with message of 6 bytes as expected by master -} +### printf() -void setup() -{ - Wire.begin(2); // join i2c bus with address #2 - Wire.onRequest(requestEvent); // register event -} +See [`Keyboard.write()`](#write--1) and [`Serial.printf()`](#printf-) documentation. -void loop() -{ - delay(100); -} -``` +### printlnf() -{{#unless core}} +See [`Keyboard.write()`](#write--1) and [`Serial.printlnf()`](#printlnf-) documentation. -## CAN (CANbus) +{{/if}} {{!-- has-usb-hid --}} -![CAN bus](/assets/images/can.png) +{{#if has-spi}} +SPI +---- +This library allows you to communicate with SPI devices, with the {{device}} as the master device. -*Since 0.4.9* +{{#if has-spi-slave}} +_Since 0.5.0_ the {{device}} can function as a slave. +{{/if}} -Controller area network (CAN bus) is a bus used in most automobiles, as well as some industrial equipment, for communication between different microcontrollers. +{{#if core}} +![SPI](/assets/images/core-pin-spi.jpg) +{{/if}} -The Photon and Electron support communicating with CAN devices via the CAN bus. +{{#if has-embedded}} -- The Photon and Electron have a CANbus on pins D1 (CAN2_TX) and D2 (CAN2_RX). -- The Electron only, has a second CANbus on pins C4 (CAN1_TX) and C5 (CAN1_TX). +The hardware SPI pin functions, which can +be used via the `SPI` object, are mapped as follows: +* `SS` => `A2` (default) +* `SCK` => `A3` +* `MISO` => `A4` +* `MOSI` => `A5` -**Note**: an additional CAN transceiver integrated circuit is needed to convert the logic-level voltages of the Photon or Electron to the voltage levels of the CAN bus. +{{#if has-multiple-spi}} +There is a second hardware SPI interface available, which can +be used via the `SPI1` object. This second port is mapped as follows: +* `SS` => `D5` (default) +* `SCK` => `D4` +* `MISO` => `D3` +* `MOSI` => `D2` +{{/if}} -On the Photon or Electron, connect pin D1 to the TX pin of the CAN transceiver and pin D2 to the RX pin. +{{#if electron}} +Additionally on the Electron, there is an alternate pin location for the second SPI interface, which can +be used via the `SPI2` object. This alternate location is mapped as follows: +* `SS` => `D5` (default) +* `SCK` => `C3` +* `MISO` => `C2` +* `MOSI` => `C1` +{{/if}} -On the Electron only, connect pin C4 to the TX pin of the CAN transceiver and pin C5 to the RX pin. +{{#if has-multiple-spi}} +**Note**: Because there are multiple SPI peripherals available, be sure to use the same `SPI`,`SPI1`{{#if electron}},`SPI2`{{/if}} object with all associated functions. I.e., -``` -// EXAMPLE USAGE on pins D1 & D2 -CANChannel can(CAN_D1_D2); +Do **NOT** use **SPI**.begin() with **SPI1**.transfer(); -void setup() { - can.begin(125000); // pick the baud rate for your network - // accept one message. If no filter added by user then accept all messages - can.addFilter(0x100, 0x7FF); -} +**Do** use **SPI**.begin() with **SPI**.transfer(); +{{/if}} -void loop() { - CANMessage message; +{{/if}} {{!-- has-embedded --}} - Message.id = 0x100; - can.transmit(message); +{{#if raspberry-pi}} - delay(10); +There are dedicated pins for SPI on the Raspberry Pi: `MOSI`, `MISO`, `SCK` and 2 chip select pins `CE0` and `CE1`. - if(can.receive(message)) { - // message received - } -} -``` +**Note**: Before using the SPI interface on the Raspberry Pi, you have to enable it in hardware. In a terminal, type `sudo raspi-config`, go to `Advanced Options`, select `SPI` and answer `Yes` to enable it. Reboot the Raspberry Pi before flashing firmware that uses the SPI peripheral. -### CANMessage +It is not recommended to use the SPI pins for general purpose IO. If you need to, you must disable the SPI peripheral in `raspi-config`, reboot and use the `MOSI`, `MISO`, `SCK`, `CE0` and `CE1` pins with `pinMode`, `digitalRead` or `digitalWrite`. +{{/if}} {{!-- raspberry-pi --}} -The CAN message struct has these members: +### begin() -``` -struct CANMessage -{ - uint32_t id; - bool extended; - bool rtr; - uint8_t len; - uint8_t data[8]; -} -``` +Initializes the SPI bus by setting SCK, MOSI, and a user-specified slave-select pin to outputs, MISO to input. SCK is pulled either high or low depending on the configured SPI data mode (default high for `SPI_MODE3`). Slave-select is pulled high. -### CANChannel - -Create a `CANChannel` global object to connect to a CAN bus on the specified pins. +**Note:** The SPI firmware ONLY initializes the user-specified slave-select pin as an `OUTPUT`. The user's code must control the slave-select pin with `digitalWrite()` before and after each SPI transfer for the desired SPI slave device. Calling `SPI.end()` does NOT reset the pin mode of the SPI pins. ```C++ // SYNTAX -CANChannel can(pins, rxQueueSize, txQueueSize); +SPI.begin(ss); +{{#if has-multiple-spi}} +SPI1.begin(ss); +{{#if electron}} +SPI2.begin(ss); +{{/if}} +{{/if}} ``` -Parameters: - -- `pins`: the Photon and Electron support pins `CAN_D1_D2`, and the Electron only, supports pins `CAN_C4_C5` -- `rxQueueSize` (optional): the receive queue size (default 32 message) -- `txQueueSize` (optional): the transmit queue size (default 32 message) +Where, the parameter `ss` is the `SPI` device slave-select pin to initialize. If no pin is specified, the default pin is `SS (A2)`. +{{#if has-multiple-spi}} +For `SPI1`, the default `ss` pin is `SS (D5)`. +{{#if electron}} +For `SPI2`, the default `ss` pin is also `SS (D5)`. +{{/if}} ```C++ -// EXAMPLE -CANChannel can(CAN_D1_D2); -// Buffer 10 received messages and 5 transmitted messages -CANChannel can(CAN_D1_D2, 10, 5); +// Example using SPI1, with D5 as the SS pin: +SPI1.begin(); +// or +SPI1.begin(D5); +``` +{{#if electron}} +```C++ +// Example using SPI2, with C0 as the SS pin: +SPI2.begin(C0); ``` +{{/if}} +{{/if}} -### begin() +{{#if has-spi-slave}} -Joins the bus at the given `baud` rate. +### begin(SPI_Mode, uint16_t) -```C++ -// SYNTAX -can.begin(baud, flags); -``` +_Since 0.5.0_ + +Initializes the {{device}} SPI peripheral in master or slave mode. + +**Note:** MISO, MOSI and SCK idle in high-impedance state when SPI peripheral is configured in slave mode and the device is not selected. Parameters: -- `baud`: common baud rates are 50000, 100000, 125000, 250000, 500000, 1000000 -- `flags` (optional): `CAN_TEST_MODE` to run the CAN bus in test mode where every transmitted message will be received back +- `mode`: `SPI_MODE_MASTER` or `SPI_MODE_SLAVE` +- `ss_pin`: slave-select pin to initialize. If no pin is specified, the default pin is `SS (A2)`. For `SPI1`, the default pin is `SS (D5)`. {{#if electron}}For `SPI2`, the default pin is also `SS (D5)`.{{/if}} ```C++ -// EXAMPLE -CANChannel can(CAN_D1_D2); -can.begin(500000); -// Use for testing without a CAN transceiver -can.begin(500000, CAN_TEST_MODE); +// Example using SPI in master mode, with A2 (default) as the SS pin: +SPI.begin(SPI_MODE_MASTER); +// Example using SPI1 in slave mode, with D5 as the SS pin +SPI1.begin(SPI_MODE_SLAVE, D5); +// Example using SPI2 in slave mode, with C0 as the SS pin +SPI2.begin(SPI_MODE_SLAVE, C0); ``` +{{/if}} {{!-- has-spi-slave --}} + ### end() -Disconnect from the bus. +Disables the SPI bus (leaving pin modes unchanged). -``` +```C++ // SYNTAX -CANChannel can(CAN_D1_D2); -can.end(); +SPI.end(); +{{#if has-multiple-spi}} +SPI1.end(); +{{#if electron}} +SPI2.end(); +{{/if}} +{{/if}} ``` -### available() - -The number of received messages waiting in the receive queue. +### setBitOrder() -Returns: `uint8_t` : the number of messages. +Sets the order of the bits shifted out of and into the SPI bus, either LSBFIRST (least-significant bit first) or MSBFIRST (most-significant bit first). -``` +```C++ // SYNTAX -uint8_t count = can.available(); +SPI.setBitOrder(order); +{{#if has-multiple-spi}} +SPI1.setBitOrder(order); +{{#if electron}} +SPI2.setBitOrder(order); +{{/if}} +{{/if}} ``` -``` -// EXAMPLE -CANChannel can(CAN_D1_D2); -if(can.available() > 0) { - // there are messages waiting -} -``` +Where, the parameter `order` can either be `LSBFIRST` or `MSBFIRST`. -### receive() +### setClockSpeed -Take a received message from the receive queue. This function does not wait for a message to arrive. +Sets the SPI clock speed. The value can be specified as a direct value, or as +as a value plus a multiplier. -``` + +```C++ // SYNTAX -can.receive(message); +SPI.setClockSpeed(value, scale); +SPI.setClockSpeed(frequency); +{{#if has-multiple-spi}} +SPI1.setClockSpeed(value, scale); +SPI1.setClockSpeed(frequency); +{{#if electron}} +SPI2.setClockSpeed(value, scale); +SPI2.setClockSpeed(frequency); +{{/if}} +{{/if}} ``` -Parameters: - -- `message`: where the received message will be copied - -Returns: boolean `true` if a message was received, `false` if the receive queue was empty. - ``` // EXAMPLE -CANChannel can(CAN_D1_D2); -CANMessage message; -if(can.receive(message)) { - Serial.println(message.id); - Serial.println(message.len); -} +// Set the clock speed as close to 15MHz (but not over) +SPI.setClockSpeed(15, MHZ); +SPI.setClockSpeed(15000000); ``` -### transmit() - -Add a message to the queue to be transmitted to the CAN bus as soon as possible. - -``` -// SYNTAX -can.transmit(message); -``` +The clock speed cannot be set to any arbitrary value, but is set internally by using a +divider (see `SPI.setClockDivider()`) that gives the highest clock speed not greater +than the one specified. -Parameters: +This method can make writing portable code easier, since it specifies the clock speed +absolutely, giving comparable results across devices. In contrast, specifying +the clock speed using dividers is typically not portable since is dependent upon the system clock speed. -- `message`: the message to be transmitted +{{#if raspberry-pi}} +On the Raspberry Pi, the default SPI clock is 4 MHz. +{{/if}} -Returns: boolean `true` if the message was added to the queue, `false` if the transmit queue was full. +### setClockDividerReference -``` -// EXAMPLE -CANChannel can(CAN_D1_D2); -CANMessage message; -message.id = 0x100; -message.length = 1; -message.data[0] = 42; -can.transmit(message); -``` +This function aims to ease porting code from other platforms by setting the clock speed that +`SPI.setClockDivider` is relative to. -**Note**: Since the CAN bus requires at least one other CAN node to acknowledge transmitted messages if the Photon or Electron is alone on the bus (such as when using a CAN shield with no other CAN node connected) then messages will never be transmitted and the transmit queue will fill up. +For example, when porting an Arduino SPI library, each to `SPI.setClockDivider()` would +need to be changed to reflect the system clock speed of the device being used. -### addFilter() +This can be avoided by placing a call to `SPI.setClockDividerReference()` before the other SPI calls. -Filter which messages will be added to the receive queue. +```cpp -``` -// SYNTAX -can.addFilter(id, mask); -can.addFilter(id, mask, type); -``` +// setting divider reference -By default all messages are received. When filters are added, only messages matching the filters will be received. Others will be discarded. +// place this early in the library code +SPI.setClockDividerReference(SPI_CLK_ARDUINO); -Parameters: +// then all following calls to setClockDivider() will give comparable clock speeds +// to running on the Arduino Uno -- `id`: the id pattern to match -- `mask`: the mask pattern to match -- `type` (optional): `CAN_FILTER_STANDARD` (default) or `CAN_FILTER_EXTENDED` +// sets the clock to as close to 4MHz without going over. +SPI.setClockDivider(SPI_CLK_DIV4); +``` -Returns: boolean `true` if the filter was added, `false` if there are too many filters (14 filters max). +The default clock divider reference is the system clock. +{{#if core}} +On the Core, this is 72 MHz. +{{else}} +{{#if raspberry-pi}} +On the Raspberry Pi, this is 64 MHz. +{{else}} +On the Photon and Electron, the system clock speeds are: +- SPI - 60 MHz +- SPI1 - 30 MHz +{{/if}} +{{/if}} -``` -// EXAMPLES -CANChannel can(CAN_D1_D2); -// Match only message ID 0x100 -can.addFilter(0x100, 0x7FF); -// Match any message with the highest ID bit set -can.addFilter(0x400, 0x400); -// Match any message with the higest ID bit cleared -can.addFilter(0x0, 0x400); -// Match only messages with extended IDs -can.addFilter(0, 0, CAN_FILTER_EXTENDED); -``` -### clearFilters() +### setClockDivider() -Clear filters and accept all messages. +Sets the SPI clock divider relative to the selected clock reference. The available dividers are 2, 4, 8, 16, 32, 64, 128 or 256. The default setting is SPI_CLOCK_DIV4, which sets the SPI clock to one-quarter the frequency of the system clock. -``` +```C++ // SYNTAX -CANChannel can(CAN_D1_D2); -can.clearFilters(); +SPI.setClockDivider(divider); +{{#if has-multiple-spi}} +SPI1.setClockDivider(divider); +{{#if electron}} +SPI2.setClockDivider(divider); +{{/if}} +{{/if}} ``` +Where the parameter, `divider` can be: -### isEnabled() + - `SPI_CLOCK_DIV2` + - `SPI_CLOCK_DIV4` + - `SPI_CLOCK_DIV8` + - `SPI_CLOCK_DIV16` + - `SPI_CLOCK_DIV32` + - `SPI_CLOCK_DIV64` + - `SPI_CLOCK_DIV128` + - `SPI_CLOCK_DIV256` -Used to check if the CAN bus is enabled already. Check if enabled before calling can.begin() again. +### setDataMode() -``` +Sets the SPI data mode: that is, clock polarity and phase. See the [Wikipedia article on SPI](http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus) for details. + +```C++ // SYNTAX -CANChannel can(CAN_D1_D2); -can.isEnabled(); +SPI.setDataMode(mode); +{{#if has-multiple-spi}} +SPI1.setDataMode(mode); +{{#if electron}} +SPI2.setDataMode(mode); +{{/if}} +{{/if}} ``` +Where the parameter, `mode` can be: -Returns: boolean `true` if the CAN bus is enabled, `false` if the CAN bus is disabled. + - `SPI_MODE0` + - `SPI_MODE1` + - `SPI_MODE2` + - `SPI_MODE3` -### errorStatus() +### transfer() -Get the current error status of the CAN bus. +Transfers one byte over the SPI bus, both sending and receiving. -``` +```C++ // SYNTAX -int status = can.errorStatus(); -``` +SPI.transfer(val); +{{#if has-multiple-spi}} +SPI1.transfer(val); +{{#if electron}} +SPI2.transfer(val); +{{/if}} +{{/if}} +``` +Where the parameter `val`, can is the byte to send out over the SPI bus. -Returns: int `CAN_NO_ERROR` when everything is ok, `CAN_ERROR_PASSIVE` when not attempting to transmit messages but still acknowledging messages, `CAN_BUS_OFF` when not transmitting or acknowledging messages. +{{#unless core}} +### transfer(void\*, void\*, size_t, std::function) -``` -// EXAMPLE -CANChannel can(CAN_D1_D2); -if(can.errorStatus() == CAN_BUS_OFF) { - Serial.println("Not properly connected to CAN bus"); -} +For transferring a large number of bytes, this form of transfer() uses DMA to speed up SPI data transfer and at the same time allows you to run code in parallel to the data transmission. The function initializes, configures and enables the DMA peripheral’s channel and stream for the selected SPI peripheral for both outgoing and incoming data and initiates the data transfer. If a user callback function is passed then it will be called after completion of the DMA transfer. This results in asynchronous filling of RX buffer after which the DMA transfer is disabled till the transfer function is called again. If NULL is passed as a callback then the result is synchronous i.e. the function will only return once the DMA transfer is complete. + +**Note**: The SPI protocol is based on a one byte OUT / one byte IN interface. For every byte expected to be received, one (dummy, typically 0x00 or 0xFF) byte must be sent. + +```C++ +// SYNTAX +SPI.transfer(tx_buffer, rx_buffer, length, myFunction); +{{#unless core}} +SPI1.transfer(tx_buffer, rx_buffer, length, myFunction); +{{/unless}} +{{#if electron}} +SPI2.transfer(tx_buffer, rx_buffer, length, myFunction); +{{/if}} ``` -This value is only updated when attempting to transmit messages. +Parameters: -The two most common causes of error are: being alone on the bus (such as when using a CAN shield not connected to anything) or using the wrong baud rate. Attempting to transmit in those situations will result in `CAN_BUS_OFF`. +- `tx_buffer`: array of Tx bytes that is filled by the user before starting the SPI transfer. If `NULL`, default dummy 0xFF bytes will be clocked out. +- `rx_buffer`: array of Rx bytes that will be filled by the slave during the SPI transfer. If `NULL`, the received data will be discarded. +- `length`: number of data bytes that are to be transferred +- `myFunction`: user specified function callback to be called after completion of the SPI DMA transfer -Errors heal automatically when properly communicating with other microcontrollers on the CAN bus. -{{/unless}} +NOTE: `tx_buffer` and `rx_buffer` sizes MUST be identical (of size `length`) -## IPAddress +_Since 0.5.0_ When SPI peripheral is configured in slave mode, the transfer will be canceled when the master deselects this slave device. The user application can check the actual number of bytes received/transmitted by calling `available()`. -Creates an IP address that can be used with TCPServer, TCPClient, and UDP objects. +### transferCancel() -```C++ -// EXAMPLE USAGE +_Since 0.5.0_ -IPAddress localIP; -IPAddress server(8,8,8,8); -IPAddress IPfromInt( 167772162UL ); // 10.0.0.2 as 10*256^3+0*256^2+0*256+2 -uint8_t server[] = { 10, 0, 0, 2}; -IPAddress IPfromBytes( server ); -``` +Aborts the configured DMA transfer and disables the DMA peripheral’s channel and stream for the selected SPI peripheral for both outgoing and incoming data. -The IPAddress also allows for comparisons. +**Note**: The user specified SPI DMA transfer completion function will still be called to indicate the end of DMA transfer. The user application can check the actual number of bytes received/transmitted by calling `available()`. -```C++ -if (IPfromInt == IPfromBytes) -{ - Serial.println("Same IP addresses"); -} -``` +### onSelect() -You can also use indexing the get or change individual bytes in the IP address. +_Since 0.5.0_ + +Registers a function to be called when the SPI master selects or deselects this slave device by pulling configured slave-select pin low (selected) or high (deselected). + +Parameters: `handler`: the function to be called when the slave is selected or deselected; this should take a single uint8_t parameter (the current state: `1` - selected, `0` - deselected) and return nothing, e.g.: `void myHandler(uint8_t state)` ```C++ -// PING ALL HOSTS ON YOUR SUBNET EXCEPT YOURSELF -IPAddress localIP = WiFi.localIP(); -uint8_t myLastAddrByte = localIP[3]; -for(uint8_t ipRange=1; ipRange<255; ipRange++) -{ - if (ipRange != myLastAddrByte) - { - localIP[3] = ipRange; - WiFi.ping(localIP); - } +// SPI slave example +static uint8_t rx_buffer[64]; +static uint8_t tx_buffer[64]; +static uint32_t select_state = 0x00; +static uint32_t transfer_state = 0x00; + +void onTransferFinished() { + transfer_state = 1; } -``` -You can also assign to an IPAddress from an array of uint8's or a 32-bit unsigned integer. +void onSelect(uint8_t state) { + if (state) + select_state = state; +} -```C++ -IPAddress IPfromInt; // 10.0.0.2 as 10*256^3+0*256^2+0*256+2 -IPfromInt = 167772162UL; -uint8_t server[] = { 10, 0, 0, 2}; -IPAddress IPfromBytes; -IPfromBytes = server; -``` +/* executes once at startup */ +void setup() { + Serial.begin(9600); + for (int i = 0; i < sizeof(tx_buffer); i++) + tx_buffer[i] = (uint8_t)i; + SPI.onSelect(onSelect); + SPI.begin(SPI_MODE_SLAVE, A2); +} -Finally IPAddress can be used directly with print. +/* executes continuously after setup() runs */ +void loop() { + while (1) { + while(select_state == 0); + select_state = 0; -```C++ -// PRINT THE DEVICE'S IP ADDRESS IN -// THE FORMAT 192.168.0.10 -IPAddress myIP = WiFi.localIP(); -Serial.println(myIP); // prints the device's IP address + transfer_state = 0; + SPI.transfer(tx_buffer, rx_buffer, sizeof(rx_buffer), onTransferFinished); + while(transfer_state == 0); + if (SPI.available() > 0) { + Serial.printf("Received %d bytes", SPI.available()); + Serial.println(); + for (int i = 0; i < SPI.available(); i++) { + Serial.printf("%02x ", rx_buffer[i]); + } + Serial.println(); + } + } +} ``` -## TCPServer +### available() -Create a server that listens for incoming connections on the specified port. +_Since 0.5.0_ + +Returns the number of bytes available for reading in the `rx_buffer` supplied in `transfer()`. In general, returns the actual number of bytes received/transmitted during the ongoing or finished DMA transfer. ```C++ // SYNTAX -TCPServer server = TCPServer(port); +SPI.available(); ``` -Parameters: `port`: the port to listen on (`int`) +Returns the number of bytes available. -```C++ -// EXAMPLE USAGE +{{/unless}} {{!-- core --}} -// telnet defaults to port 23 -TCPServer server = TCPServer(23); -TCPClient client; +{{/if}} {{!-- has-spi --}} -void setup() -{ - // start listening for clients - server.begin(); +{{#if has-i2c}} - // Make sure your Serial Terminal app is closed before powering your device - Serial.begin(9600); - // Now open your Serial Terminal, and hit any key to continue! - while(!Serial.available()) Particle.process(); +Wire (I2C) +---- - Serial.println(WiFi.localIP()); - Serial.println(WiFi.subnetMask()); - Serial.println(WiFi.gatewayIP()); - Serial.println(WiFi.SSID()); -} +{{#if has-embedded}} +{{#unless electron}} +![I2C](/assets/images/core-pin-i2c.jpg) +{{/unless}} +{{/if}} -void loop() -{ - if (client.connected()) { - // echo all available bytes back to the client - while (client.available()) { - server.write(client.read()); - } - } else { - // if no client is yet connected, check for a new connection - client = server.available(); - } -} -``` +This library allows you to communicate with I2C / TWI(Two Wire Interface) devices. -### begin() +{{#if has-embedded}} +On the Core/Photon/Electron, D0 is the Serial Data Line (SDA) and D1 is the Serial Clock (SCL). {{#if electron}}Additionally on the Electron, there is an alternate pin location for the I2C interface: C4 is the Serial Data Line (SDA) and C5 is the Serial Clock (SCL).{{/if}} Both SCL and SDA pins are open-drain outputs that only pull LOW and typically operate with 3.3V logic, but are tolerant to 5V. Connect a pull-up resistor(1.5k to 10k) on the SDA line to 3V3. Connect a pull-up resistor(1.5k to 10k) on the SCL line to 3V3. If you are using a breakout board with an I2C peripheral, check to see if it already incorporates pull-up resistors. -Tells the server to begin listening for incoming connections. +These pins are used via the `Wire` object. -```C++ -// SYNTAX -server.begin(); -``` +* `SCL` => `D1` +* `SDA` => `D0` -### available() +{{#if electron}} +Additionally on the Electron, there is an alternate pin location for the I2C interface, which can +be used via the `Wire1` object. This alternate location is mapped as follows: +* `SCL` => `C5` +* `SDA` => `C4` -Gets a client that is connected to the server and has data available for reading. The connection persists when the returned client object goes out of scope; you can close it by calling `client.stop()`. +**Note**: Because there are multiple I2C locations available, be sure to use the same `Wire` or `Wire1` object with all associated functions. I.e., -`available()` inherits from the `Stream` utility class. +Do **NOT** use **Wire**.begin() with **Wire1**.write(); -### write() +**Do** use **Wire1**.begin() with **Wire1**.transfer(); +{{/if}} -Write data to the last client that connected to a server. This data is sent as a byte or series of bytes. +{{/if}} {{!-- has-embedded --}} + +{{#if raspberry-pi}} +There are dedicated pins for I2C on the Raspberry Pi: Serial Data Line (SDA) and Serial Clock (SCL). [See the pin out diagram](/datasheets/raspberrypi-datasheet/#pin-out-diagram) to find out where pins are located. + +**Note**: Before using the I2C interface on the Raspberry Pi, you have to enable it in hardware. In a terminal, type `sudo raspi-config`, go to `Advanced Options`, select `I2C` and answer `Yes` to enable it. Reboot the Raspberry Pi before flashing firmware that uses the I2C peripheral. + +It is not recommended to use the I2C pins for general purpose IO. If you need to, you must disable the I2C peripheral in `raspi-config`, reboot and use the `SCL` and `SDA` pins with `pinMode`, `digitalRead` or `digitalWrite`. +{{/if}} + +{{#if has-embedded}} + +### setSpeed() + +Sets the I2C clock speed. This is an optional call (not from the original Arduino specs.) and must be called once before calling begin(). The default I2C clock speed is 100KHz and the maximum clock speed is 400KHz. ```C++ // SYNTAX -server.write(val); -server.write(buf, len); +Wire.setSpeed(clockSpeed); +Wire.begin(); ``` Parameters: -- `val`: a value to send as a single byte (byte or char) -- `buf`: an array to send as a series of bytes (byte or char) -- `len`: the length of the buffer - -Returns: `byte`: `write()` returns the number of bytes written. It is not necessary to read this. +- `clockSpeed`: CLOCK_SPEED_100KHZ, CLOCK_SPEED_400KHZ or a user specified speed in hertz (e.g. `Wire.setSpeed(20000)` for 20kHz) -### print() +### stretchClock() -Print data to the last client connected to a server. Prints numbers as a sequence of digits, each an ASCII character (e.g. the number 123 is sent as the three characters '1', '2', '3'). +Enables or Disables I2C clock stretching. This is an optional call (not from the original Arduino specs.) and must be called once before calling begin(). I2C clock stretching is only used with I2C Slave mode. The default I2C clock stretching mode is enabled. ```C++ // SYNTAX -server.print(data); -server.print(data, BASE) ; +Wire.stretchClock(stretch); +Wire.begin(4); // I2C Slave mode, address #4 ``` Parameters: -- `data`: the data to print (char, byte, int, long, or string) -- `BASE`(optional): the base in which to print numbers: BIN for binary (base 2), DEC for decimal (base 10), OCT for octal (base 8), HEX for hexadecimal (base 16). +- `stretch`: boolean. `true` will enable clock stretching (default). `false` will disable clock stretching. -Returns: `byte`: `print()` will return the number of bytes written, though reading that number is optional +{{/if}} {{!-- has-embedded --}} -### println() +### begin() -Print data, followed by a newline, to the last client connected to a server. Prints numbers as a sequence of digits, each an ASCII character (e.g. the number 123 is sent as the three characters '1', '2', '3'). +Initiate the Wire library and join the I2C bus as a master{{#if has-i2c-slave}} or slave{{/if}}. This should normally be called only once. ```C++ // SYNTAX -server.println(); -server.println(data); -server.println(data, BASE) ; +Wire.begin(); +{{#if has-i2c-slave}} +Wire.begin(address); +{{/if}} {{!-- has-i2c-slave --}} ``` -Parameters: +{{#if has-i2c-slave}} +Parameters: `address`: the 7-bit slave address (optional); if not specified, join the bus as an I2C master. If address is specified, join the bus as an I2C slave. +{{/if}} {{!-- has-i2c-slave --}} -- `data` (optional): the data to print (char, byte, int, long, or string) -- `BASE` (optional): the base in which to print numbers: BIN for binary (base 2), DEC for decimal (base 10), OCT for octal (base 8), HEX for hexadecimal (base 16). +### end() -## TCPClient +_Since 0.4.6_ -Creates a client which can connect to a specified internet IP address and port (defined in the `client.connect()` function). +Releases the I2C bus so that the pins used by the I2C bus are available for general purpose I/O. + +### isEnabled() + +Used to check if the Wire library is enabled already. Useful if using multiple slave devices on the same I2C bus. Check if enabled before calling Wire.begin() again. ```C++ // SYNTAX -TCPClient client; +Wire.isEnabled(); ``` +Returns: boolean `true` if I2C enabled, `false` if I2C disabled. + ```C++ // EXAMPLE USAGE -TCPClient client; -byte server[] = { 74, 125, 224, 72 }; // Google -void setup() -{ - // Make sure your Serial Terminal app is closed before powering your device - Serial.begin(9600); - // Now open your Serial Terminal, and hit any key to continue! - while(!Serial.available()) Particle.process(); - - Serial.println("connecting..."); - - if (client.connect(server, 80)) - { - Serial.println("connected"); - client.println("GET /search?q=unicorn HTTP/1.0"); - client.println("Host: www.google.com"); - client.println("Content-Length: 0"); - client.println(); - } - else - { - Serial.println("connection failed"); - } -} - -void loop() -{ - if (client.available()) - { - char c = client.read(); - Serial.print(c); - } - - if (!client.connected()) - { - Serial.println(); - Serial.println("disconnecting."); - client.stop(); - for(;;); - } +// Initialize the I2C bus if not already enabled +if (!Wire.isEnabled()) { + Wire.begin(); } ``` -### connected() +### requestFrom() -Whether or not the client is connected. Note that a client is considered connected if the connection has been closed but there is still unread data. +Used by the master to request bytes from a slave device. The bytes may then be retrieved with the `available()` and `read()` functions. ```C++ // SYNTAX -client.connected(); +Wire.requestFrom(address, quantity); +Wire.requestFrom(address, quantity, stop); ``` -Returns true if the client is connected, false if not. +Parameters: -### connect() +- `address`: the 7-bit address of the device to request bytes from +- `quantity`: the number of bytes to request (Max. 32) +- `stop`: boolean. `true` will send a stop message after the request, releasing the bus. `false` will continually send a restart after the request, keeping the connection active. The bus will not be released, which prevents another master device from transmitting between messages. This allows one master device to send multiple transmissions while in control. If no argument is specified, the default value is `true`. -Connects to a specified IP address and port. The return value indicates success or failure. Also supports DNS lookups when using a domain name. +Returns: `byte` : the number of bytes returned from the slave device. If a timeout occurs, will return `0`. -```C++ -// SYNTAX -client.connect(); -client.connect(ip, port); -client.connect(URL, port); -``` +{{#if has-embedded}} -Parameters: +### reset() -- `ip`: the IP address that the client will connect to (array of 4 bytes) -- `URL`: the domain name the client will connect to (string, ex.:"particle.io") -- `port`: the port that the client will connect to (`int`) +_Since 0.4.6_ -Returns true if the connection succeeds, false if not. +Attempts to reset the I2C bus. This should be called only if the I2C bus has +has hung. In 0.4.6 additional rework was done for the I2C bus on the Photon and Electron, so +we hope this function isn't required, and it's provided for completeness. -### write() +{{/if}} {{!-- has-embedded --}} -Write data to the server the client is connected to. This data is sent as a byte or series of bytes. +### beginTransmission() + +Begin a transmission to the I2C slave device with the given address. Subsequently, queue bytes for transmission with the `write()` function and transmit them by calling `endTransmission()`. ```C++ // SYNTAX -client.write(val); -client.write(buf, len); +Wire.beginTransmission(address); ``` -Parameters: - -- `val`: a value to send as a single byte (byte or char) -- `buf`: an array to send as a series of bytes (byte or char) -- `len`: the length of the buffer +Parameters: `address`: the 7-bit address of the device to transmit to. -Returns: `byte`: `write()` returns the number of bytes written. It is not necessary to read this value. +### endTransmission() -### print() +Ends a transmission to a slave device that was begun by `beginTransmission()` and transmits the bytes that were queued by `write()`. -Print data to the server that a client is connected to. Prints numbers as a sequence of digits, each an ASCII character (e.g. the number 123 is sent as the three characters '1', '2', '3'). ```C++ // SYNTAX -client.print(data); -client.print(data, BASE) ; +Wire.endTransmission(); +Wire.endTransmission(stop); ``` -Parameters: +Parameters: `stop` : boolean. +`true` will send a stop message after the last byte, releasing the bus after transmission. `false` will send a restart, keeping the connection active. The bus will not be released, which prevents another master device from transmitting between messages. This allows one master device to send multiple transmissions while in control. If no argument is specified, the default value is `true`. -- `data`: the data to print (char, byte, int, long, or string) -- `BASE`(optional): the base in which to print numbers: BIN for binary (base 2), DEC for decimal (base 10), OCT for octal (base 8), HEX for hexadecimal (base 16). +Returns: `byte`, which indicates the status of the transmission: -Returns: `byte`: `print()` will return the number of bytes written, though reading that number is optional +- 0: success +- 1: busy timeout upon entering endTransmission() +- 2: START bit generation timeout +- 3: end of address transmission timeout +- 4: data byte transfer timeout +- 5: data byte transfer succeeded, busy timeout immediately after -### println() +### write() -Print data, followed by a carriage return and newline, to the server a client is connected to. Prints numbers as a sequence of digits, each an ASCII character (e.g. the number 123 is sent as the three characters '1', '2', '3'). +Queues bytes for transmission from a master to slave device (in-between calls to `beginTransmission()` and `endTransmission()`){{#if has-i2c-slave}}, or writes data from a slave device in response to a request from a master{{/if}}. Buffer size is truncated to 32 bytes; writing bytes beyond 32 before calling endTransmission() will be ignored. ```C++ // SYNTAX -client.println(); -client.println(data); -client.println(data, BASE) ; +Wire.write(value); +Wire.write(string); +Wire.write(data, length); ``` - Parameters: -- `data` (optional): the data to print (char, byte, int, long, or string) -- `BASE` (optional): the base in which to print numbers: BIN for binary (base 2), DEC for decimal (base 10), OCT for octal (base 8), HEX for hexadecimal (base 16). +- `value`: a value to send as a single byte +- `string`: a string to send as a series of bytes +- `data`: an array of data to send as bytes +- `length`: the number of bytes to transmit (Max. 32) -### available() +Returns: `byte` -Returns the number of bytes available for reading (that is, the amount of data that has been written to the client by the server it is connected to). +`write()` will return the number of bytes written, though reading that number is optional. ```C++ -// SYNTAX -client.available(); +// EXAMPLE USAGE + +// Master Writer running on Device No.1 (Use with corresponding Slave Reader running on Device No.2) + +void setup() { + Wire.begin(); // join i2c bus as master +} + +byte x = 0; + +void loop() { + Wire.beginTransmission(4); // transmit to slave device #4 + Wire.write("x is "); // sends five bytes + Wire.write(x); // sends one byte + Wire.endTransmission(); // stop transmitting + + x++; + delay(500); +} ``` -Returns the number of bytes available. +### available() -### read() -Read the next byte received from the server the client is connected to (after the last call to `read()`). +Returns the number of bytes available for retrieval with `read()`. This should be called on a master device after a call to `requestFrom()`{{#if has-i2c-slave}} or on a slave inside the `onReceive()` handler{{/if}}. ```C++ -// SYNTAX -client.read(); +Wire.available(); ``` -Returns the next byte (or character), or -1 if none is available. +Returns: The number of bytes available for reading. -### flush() +### read() -Discard any bytes that have been written to the client but not yet read. +Reads a byte that was transmitted from a slave device to a master after a call to `requestFrom()`{{#if has-i2c-slave}} or was transmitted from a master to a slave{{/if}}. `read()` inherits from the `Stream` utility class. ```C++ // SYNTAX -client.flush(); +Wire.read() ; ``` -### remoteIP() - -_Since 0.4.5_ - -Retrieves the remote `IPAddress` of a connected `TCPClient`. When the `TCPClient` is retrieved -from `TCPServer.available()` (where the client is a remote client connecting to a local server) the -`IPAddress` gives the remote address of the connecting client. - -When `TCPClient` was created directly via `TCPClient.connect()`, then `remoteIP` -returns the remote server the client is connected to. +Returns: The next byte received ```C++ +// EXAMPLE USAGE -// EXAMPLE - TCPClient from TCPServer - -TCPServer server(80); -// ... +// Master Reader running on Device No.1 (Use with corresponding Slave Writer running on Device No.2) -void setup() -{ - Serial.begin(9600); - server.begin(80); +void setup() { + Wire.begin(); // join i2c bus as master + Serial.begin(9600); // start serial for output } -void loop() -{ - // check for a new client to our server - TCPClient client = server.available(); - if (client.connected()) - { - // we got a new client - // find where the client's remote address - IPAddress clientIP = client.remoteIP(); - // print the address to Serial - Serial.println(clientIP); - } -} -``` +void loop() { + Wire.requestFrom(2, 6); // request 6 bytes from slave device #2 -```C++ -// EXAMPLE - TCPClient.connect() + while(Wire.available()){ // slave may send less than requested + char c = Wire.read(); // receive a byte as character + Serial.print(c); // print the character + } -TCPClient client; -client.connect("www.google.com", 80); -if (client.connected()) -{ - IPAddress clientIP = client.remoteIP(); - // IPAddress equals whatever www.google.com resolves to + delay(500); } - ``` +### peek() -### stop() - -Disconnect from the server. +Similar in use to read(). Reads (but does not remove from the buffer) a byte that was transmitted from a slave device to a master after a call to `requestFrom()`{{#if has-i2c-slave}} or was transmitted from a master to a slave{{/if}}. `read()` inherits from the `Stream` utility class. Useful for peeking at the next byte to be read. ```C++ // SYNTAX -client.stop(); +Wire.peek(); ``` +Returns: The next byte received (without removing it from the buffer) -## UDP +{{#if has-i2c-slave}} -This class enables UDP messages to be sent and received. +### onReceive() -```cpp +Registers a function to be called when a slave device receives a transmission from a master. + +Parameters: `handler`: the function to be called when the slave receives data; this should take a single int parameter (the number of bytes read from the master) and return nothing, e.g.: `void myHandler(int numBytes) ` + +```C++ // EXAMPLE USAGE -// UDP Port used for two way communication -unsigned int localPort = 8888; +// Slave Reader running on Device No.2 (Use with corresponding Master Writer running on Device No.1) -// An UDP instance to let us send and receive packets over UDP -UDP Udp; +// function that executes whenever data is received from master +// this function is registered as an event, see setup() +void receiveEvent(int howMany) { + while(1 < Wire.available()) { // loop through all but the last + char c = Wire.read(); // receive byte as a character + Serial.print(c); // print the character + } + int x = Wire.read(); // receive byte as an integer + Serial.println(x); // print the integer +} void setup() { - // start the UDP - Udp.begin(localPort); - - // Print your device IP Address via serial - Serial.begin(9600); - Serial.println(WiFi.localIP()); + Wire.begin(4); // join i2c bus with address #4 + Wire.onReceive(receiveEvent); // register event + Serial.begin(9600); // start serial for output } void loop() { - // Check if data has been received - if (Udp.parsePacket() > 0) { + delay(100); +} +``` - // Read first char of data received - char c = Udp.read(); +### onRequest() - // Ignore other chars - Udp.flush(); +Register a function to be called when a master requests data from this slave device. - // Store sender ip and port - IPAddress ipAddress = Udp.remoteIP(); - int port = Udp.remotePort(); +Parameters: `handler`: the function to be called, takes no parameters and returns nothing, e.g.: `void myHandler() ` - // Echo back data to sender - Udp.beginPacket(ipAddress, port); - Udp.write(c); - Udp.endPacket(); - } +```C++ +// EXAMPLE USAGE + +// Slave Writer running on Device No.2 (Use with corresponding Master Reader running on Device No.1) + +// function that executes whenever data is requested by master +// this function is registered as an event, see setup() +void requestEvent() { + Wire.write("hello "); // respond with message of 6 bytes as expected by master } -``` -_Note that UDP does not guarantee that messages are always delivered, or that -they are delivered in the order supplied. In cases where your application -requires a reliable connection, `TCPClient` is a simpler alternative._ +void setup() { + Wire.begin(2); // join i2c bus with address #2 + Wire.onRequest(requestEvent); // register event +} -{{#if core}} -The UDP protocol implementation has known issues that will require extra consideration when programming with it. Please refer to the Known Issues category of the Community for details. The are also numerous working examples and workarounds in the searchable Community topics. -{{/if}} +void loop() { + delay(100); +} +``` +{{/if}} {{!-- has-i2c-slave --}} -There are two primary ways of working with UDP - buffered operation and unbuffered operation. +{{/if}} {{!-- has-i2c --}} -1. buffered operation allows you to read and write packets in small pieces, since the system takes care of allocating the required buffer to hold the entire packet. - - to read a buffered packet, call `parsePacket`, then use `available` and `read` to retrieve the packet received - - to write a buffered packet, optionally call `setBuffer` to set the maximum size of the packet (the default is 512 bytes), followed by - `beginPacket`, then as many calls to `write`/`print` as necessary to build the packet contents, followed finally by `end` to send the packet over the network. +{{#if has-can}} -2. unbuffered operation allows you to read and write entire packets in a single operation - your application is responsible for allocating the buffer to contain the packet to be sent or received over the network. - - to read an unbuffered packet, call `receivePacket` with a buffer to hold the received packet. - - to write an unbuffered packet, call `sendPacket` with the packet buffer to send, and the destination address. +## CAN (CANbus) +![CAN bus](/assets/images/can.png) - - +_Since 0.4.9_ -### begin() +Controller area network (CAN bus) is a bus used in most automobiles, as well as some industrial equipment, for communication between different microcontrollers. -Initializes the UDP library and network settings. +The Photon and Electron support communicating with CAN devices via the CAN bus. -```cpp -// SYNTAX -Udp.begin(port); -``` +- The Photon and Electron have a CANbus on pins D1 (CAN2_TX) and D2 (CAN2_RX). +- The Electron only, has a second CANbus on pins C4 (CAN1_TX) and C5 (CAN1_TX). -### available() +**Note**: an additional CAN transceiver integrated circuit is needed to convert the logic-level voltages of the Photon or Electron to the voltage levels of the CAN bus. -Get the number of bytes (characters) available for reading from the buffer. This is data that's already arrived. +On the Photon or Electron, connect pin D1 to the TX pin of the CAN transceiver and pin D2 to the RX pin. -```cpp -// SYNTAX -int count = Udp.available(); -``` +On the Electron only, connect pin C4 to the TX pin of the CAN transceiver and pin C5 to the RX pin. -This function can only be successfully called after `UDP.parsePacket()`. +``` +// EXAMPLE USAGE on pins D1 & D2 +CANChannel can(CAN_D1_D2); -`available()` inherits from the `Stream` utility class. +void setup() { + can.begin(125000); // pick the baud rate for your network + // accept one message. If no filter added by user then accept all messages + can.addFilter(0x100, 0x7FF); +} -Returns the number of bytes available to read. +void loop() { + CANMessage message; -### beginPacket() + Message.id = 0x100; + can.transmit(message); -Starts a connection to write UDP data to the remote connection. + delay(10); -```cpp -// SYNTAX -Udp.beginPacket(remoteIP, remotePort); + if(can.receive(message)) { + // message received + } +} ``` -Parameters: - - - `remoteIP`: the IP address of the remote connection (4 bytes) - - `remotePort`: the port of the remote connection (int) +### CANMessage -It returns nothing. +The CAN message struct has these members: -### endPacket() +``` +struct CANMessage +{ + uint32_t id; + bool extended; + bool rtr; + uint8_t len; + uint8_t data[8]; +} +``` -Called after writing buffered UDP data using `write()` or `print()`. The buffered data is then sent to the -remote UDP peer. +### CANChannel +Create a `CANChannel` global object to connect to a CAN bus on the specified pins. -```cpp +```C++ // SYNTAX -Udp.endPacket(); +CANChannel can(pins, rxQueueSize, txQueueSize); ``` -Parameters: NONE +Parameters: -### write() +- `pins`: the Photon and Electron support pins `CAN_D1_D2`, and the Electron only, supports pins `CAN_C4_C5` +- `rxQueueSize` (optional): the receive queue size (default 32 message) +- `txQueueSize` (optional): the transmit queue size (default 32 message) -Writes UDP data to the buffer - no data is actually sent. Must be wrapped between `beginPacket()` and `endPacket()`. `beginPacket()` initializes the packet of data, it is not sent until `endPacket()` is called. +```C++ +// EXAMPLE +CANChannel can(CAN_D1_D2); +// Buffer 10 received messages and 5 transmitted messages +CANChannel can(CAN_D1_D2, 10, 5); +``` -```cpp +### begin() + +Joins the bus at the given `baud` rate. + +```C++ // SYNTAX -Udp.write(message); -Udp.write(buffer, size); +can.begin(baud, flags); ``` Parameters: - - `message`: the outgoing message (char) - - `buffer`: an array to send as a series of bytes (byte or char) - - `size`: the length of the buffer - -Returns: - - - `byte`: returns the number of characters sent. This does not have to be read +- `baud`: common baud rates are 50000, 100000, 125000, 250000, 500000, 1000000 +- `flags` (optional): `CAN_TEST_MODE` to run the CAN bus in test mode where every transmitted message will be received back +```C++ +// EXAMPLE +CANChannel can(CAN_D1_D2); +can.begin(500000); +// Use for testing without a CAN transceiver +can.begin(500000, CAN_TEST_MODE); +``` -### parsePacket() +### end() -Checks for the presence of a UDP packet, and reports the size. `parsePacket()` must be called before reading the buffer with `UDP.read()`. +Disconnect from the bus. -```cpp +``` // SYNTAX -size = Udp.parsePacket(); +CANChannel can(CAN_D1_D2); +can.end(); ``` -Parameters: NONE +### available() -Returns: +The number of received messages waiting in the receive queue. - - `int`: the size of a received UDP packet +Returns: `uint8_t` : the number of messages. -### read() +``` +// SYNTAX +uint8_t count = can.available(); +``` -Reads UDP data from the specified buffer. If no arguments are given, it will return the next character in the buffer. +``` +// EXAMPLE +CANChannel can(CAN_D1_D2); +if(can.available() > 0) { + // there are messages waiting +} +``` -This function can only be successfully called after `UDP.parsePacket()`. +### receive() -```cpp +Take a received message from the receive queue. This function does not wait for a message to arrive. + +``` // SYNTAX -count = Udp.read(); -count = Udp.read(packetBuffer, MaxSize); +can.receive(message); ``` -Parameters: - - `packetBuffer`: buffer to hold incoming packets (char) - - `MaxSize`: maximum size of the buffer (int) +Parameters: -Returns: +- `message`: where the received message will be copied - - `int`: returns the character in the buffer or -1 if no character is available +Returns: boolean `true` if a message was received, `false` if the receive queue was empty. +``` +// EXAMPLE +CANChannel can(CAN_D1_D2); +CANMessage message; +if(can.receive(message)) { + Serial.println(message.id); + Serial.println(message.len); +} +``` -### stop() +### transmit() -Disconnect from the server. Release any resource being used during the UDP session. +Add a message to the queue to be transmitted to the CAN bus as soon as possible. -```cpp +``` // SYNTAX -Udp.stop(); +can.transmit(message); ``` -Parameters: NONE +Parameters: -### remoteIP() +- `message`: the message to be transmitted -Returns the IP address of sender of the packet parsed by `Udp.parsePacket()`/`Udp.receivePacket()`. +Returns: boolean `true` if the message was added to the queue, `false` if the transmit queue was full. -```cpp -// SYNTAX -ip = Udp.remoteIP(); +``` +// EXAMPLE +CANChannel can(CAN_D1_D2); +CANMessage message; +message.id = 0x100; +message.length = 1; +message.data[0] = 42; +can.transmit(message); ``` -Parameters: NONE - -Returns: - - - IPAddress : the IP address of the sender of the packet parsed by `Udp.parsePacket()`/`Udp.receivePacket()`. +**Note**: Since the CAN bus requires at least one other CAN node to acknowledge transmitted messages if the Photon or Electron is alone on the bus (such as when using a CAN shield with no other CAN node connected) then messages will never be transmitted and the transmit queue will fill up. -### remotePort() +### addFilter() -Returns the port from which the UDP packet was sent. The packet is the one most recently processed by `Udp.parsePacket()`/`Udp.receivePacket()`. +Filter which messages will be added to the receive queue. -```cpp +``` // SYNTAX -int port = Udp.remotePort(); +can.addFilter(id, mask); +can.addFilter(id, mask, type); ``` -Parameters: NONE - -Returns: - -- `int`: the port from which the packet parsed by `Udp.parsePacket()`/`Udp.receivePacket()` was sent. +By default all messages are received. When filters are added, only messages matching the filters will be received. Others will be discarded. +Parameters: -### setBuffer() - -_Since 0.4.5_ - -Initializes the buffer used by a `UDP` instance for buffered reads/writes. The buffer -is used when your application calls `beginPacket()` and `parsePacket()`. If `setBuffer()` isn't called, -the buffer size defaults to 512 bytes, and is allocated when buffered operation is initialized via `beginPacket()` or `parsePacket()`. - -```cpp -// SYNTAX -Udp.setBuffer(size); // dynamically allocated buffer -Udp.setBuffer(size, buffer); // application provided buffer +- `id`: the id pattern to match +- `mask`: the mask pattern to match +- `type` (optional): `CAN_FILTER_STANDARD` (default) or `CAN_FILTER_EXTENDED` -// EXAMPLE USAGE - dynamically allocated buffer -UDP Udp; +Returns: boolean `true` if the filter was added, `false` if there are too many filters (14 filters max). -// uses a dynamically allocated buffer that is 1024 bytes in size -if (!Udp.setBuffer(1024)) -{ - // on no, couldn't allocate the buffer -} -else -{ - // 'tis good! -} ``` - -```cpp -// EXAMPLE USAGE - application-provided buffer -UDP Udp; - -char appBuffer[800]; -Udp.setBuffer(800, appBuffer); +// EXAMPLES +CANChannel can(CAN_D1_D2); +// Match only message ID 0x100 +can.addFilter(0x100, 0x7FF); +// Match any message with the highest ID bit set +can.addFilter(0x400, 0x400); +// Match any message with the higest ID bit cleared +can.addFilter(0x0, 0x400); +// Match only messages with extended IDs +can.addFilter(0, 0, CAN_FILTER_EXTENDED); ``` -Parameters: - -- `unsigned int`: the size of the buffer -- `pointer`: the buffer. If not provided, or `NULL` the system will attempt to - allocate a buffer of the size requested. +### clearFilters() -Returns: -- `true` when the buffer was successfully allocated, `false` if there was insufficient memory. (For application-provided buffers -the function always returns `true`.) +Clear filters and accept all messages. -### releaseBuffer() +``` +// SYNTAX +CANChannel can(CAN_D1_D2); +can.clearFilters(); +``` -_Since 0.4.5_ +### isEnabled() -Releases the buffer previously set by a call to `setBuffer()`. +Used to check if the CAN bus is enabled already. Check if enabled before calling can.begin() again. -```cpp +``` // SYNTAX -Udp.releaseBuffer(); +CANChannel can(CAN_D1_D2); +can.isEnabled(); ``` -_This is typically required only when performing advanced memory management and the UDP instance is -not scoped to the lifetime of the application._ - -### sendPacket() +Returns: boolean `true` if the CAN bus is enabled, `false` if the CAN bus is disabled. -_Since 0.4.5_ +### errorStatus() -Sends a packet, unbuffered, to a remote UDP peer. +Get the current error status of the CAN bus. -```cpp +``` // SYNTAX -Udp.sendPacket(buffer, bufferSize, remoteIP, remotePort); - -// EXAMPLE USAGE -UDP Udp; - -char buffer[] = "Particle powered"; - -IPAddress remoteIP(192, 168, 1, 100); -int port = 1337; +int status = can.errorStatus(); +``` -void setup() { - // Required for two way communication - Udp.begin(8888); +Returns: int `CAN_NO_ERROR` when everything is ok, `CAN_ERROR_PASSIVE` when not attempting to transmit messages but still acknowledging messages, `CAN_BUS_OFF` when not transmitting or acknowledging messages. - if (Udp.sendPacket(buffer, sizeof(buffer), remoteIP, port) < 0) { - Particle.publish("Error"); - } +``` +// EXAMPLE +CANChannel can(CAN_D1_D2); +if(can.errorStatus() == CAN_BUS_OFF) { + Serial.println("Not properly connected to CAN bus"); } ``` -Parameters: -- `pointer` (buffer): the buffer of data to send -- `int` (bufferSize): the number of bytes of data to send -- `IPAddress` (remoteIP): the destination address of the remote peer -- `int` (remotePort): the destination port of the remote peer +This value is only updated when attempting to transmit messages. -Returns: -- `int`: The number of bytes written. Negative value on error. +The two most common causes of error are: being alone on the bus (such as when using a CAN shield not connected to anything) or using the wrong baud rate. Attempting to transmit in those situations will result in `CAN_BUS_OFF`. -{{#if photon}} -### joinMulticast() +Errors heal automatically when properly communicating with other microcontrollers on the CAN bus. -_Since 0.4.5_ +{{/if}} {{!-- has-can --}} -Join a multicast address for all UDP sockets which are on the same network interface as this one. +## IPAddress -```cpp -// SYNTAX -Udp.joinMulticast(IPAddress& ip); +Creates an IP address that can be used with TCPServer, TCPClient, and UDP objects. +```C++ // EXAMPLE USAGE -UDP Udp; -int remotePort = 1024; -IPAddress multicastAddress(224,0,0,0); +IPAddress localIP; +IPAddress server(8,8,8,8); +IPAddress IPfromInt( 167772162UL ); // 10.0.0.2 as 10*256^3+0*256^2+0*256+2 +uint8_t server[] = { 10, 0, 0, 2}; +IPAddress IPfromBytes( server ); +``` -Udp.begin(remotePort); -Udp.joinMulticast(multicastAddress); +The IPAddress also allows for comparisons. + +```C++ +if (IPfromInt == IPfromBytes) +{ + Serial.println("Same IP addresses"); +} ``` -This will allow reception of multicast packets sent to the given address for UDP sockets -which have bound the port to which the multicast packet was sent. -Must be called only after `begin()` so that the network interface is established. +You can also use indexing the get or change individual bytes in the IP address. -### leaveMulticast() +```C++ +// PING ALL HOSTS ON YOUR SUBNET EXCEPT YOURSELF +IPAddress localIP = WiFi.localIP(); +uint8_t myLastAddrByte = localIP[3]; +for(uint8_t ipRange=1; ipRange<255; ipRange++) +{ + if (ipRange != myLastAddrByte) + { + localIP[3] = ipRange; + WiFi.ping(localIP); + } +} +``` -_Since 0.4.5_ +You can also assign to an IPAddress from an array of uint8's or a 32-bit unsigned integer. -Leaves a multicast group previously joined on a specific multicast address. +```C++ +IPAddress IPfromInt; // 10.0.0.2 as 10*256^3+0*256^2+0*256+2 +IPfromInt = 167772162UL; +uint8_t server[] = { 10, 0, 0, 2}; +IPAddress IPfromBytes; +IPfromBytes = server; +``` -```cpp -// SYNTAX -Udp.leaveMulticast(multicastAddress); +Finally IPAddress can be used directly with print. -// EXAMPLE USAGE -UDP Udp; -IPAddress multicastAddress(224,0,0,0); -Udp.leaveMulticast(multicastAddress); +```C++ +// PRINT THE DEVICE'S IP ADDRESS IN +// THE FORMAT 192.168.0.10 +IPAddress myIP = WiFi.localIP(); +Serial.println(myIP); // prints the device's IP address ``` -{{/if}} +## TCPServer -## Servo +Create a server that listens for incoming connections on the specified port. -This library allows your device to control RC (hobby) servo motors. Servos have integrated gears and a shaft that can be precisely controlled. Standard servos allow the shaft to be positioned at various angles, usually between 0 and 180 degrees. Continuous rotation servos allow the rotation of the shaft to be set to various speeds. +```C++ +// SYNTAX +TCPServer server = TCPServer(port); +``` -```cpp -// EXAMPLE CODE +Parameters: `port`: the port to listen on (`int`) -Servo myservo; // create servo object to control a servo - // a maximum of eight servo objects can be created +```C++ +// EXAMPLE USAGE -int pos = 0; // variable to store the servo position +// telnet defaults to port 23 +TCPServer server = TCPServer(23); +TCPClient client; void setup() { - myservo.attach(D0); // attaches the servo on the D0 pin to the servo object - // Only supported on pins that have PWM -} + // start listening for clients + server.begin(); + + // Make sure your Serial Terminal app is closed before powering your device + Serial.begin(9600); + // Now open your Serial Terminal, and hit any key to continue! + while(!Serial.available()) Particle.process(); + Serial.println(WiFi.localIP()); + Serial.println(WiFi.subnetMask()); + Serial.println(WiFi.gatewayIP()); + Serial.println(WiFi.SSID()); +} void loop() { - for(pos = 0; pos < 180; pos += 1) // goes from 0 degrees to 180 degrees - { // in steps of 1 degree - myservo.write(pos); // tell servo to go to position in variable 'pos' - delay(15); // waits 15ms for the servo to reach the position - } - for(pos = 180; pos>=1; pos-=1) // goes from 180 degrees to 0 degrees - { - myservo.write(pos); // tell servo to go to position in variable 'pos' - delay(15); // waits 15ms for the servo to reach the position + if (client.connected()) { + // echo all available bytes back to the client + while (client.available()) { + server.write(client.read()); + } + } else { + // if no client is yet connected, check for a new connection + client = server.available(); } } ``` -**NOTE:** Unlike Arduino, you do not need to include `Servo.h`; it is included automatically. +### begin() +Tells the server to begin listening for incoming connections. -### attach() +```C++ +// SYNTAX +server.begin(); +``` -Set up a servo on a particular pin. Note that, Servo can only be attached to pins with a timer. +### available() -- on the Core, Servo can be connected to A0, A1, A4, A5, A6, A7, D0, and D1. -- on the Photon, Servo can be connected to A4, A5, WKP, RX, TX, D0, D1, D2, D3 +Gets a client that is connected to the server and has data available for reading. The connection persists when the returned client object goes out of scope; you can close it by calling `client.stop()`. -```cpp -// SYNTAX -servo.attach(pin) -``` +`available()` inherits from the `Stream` utility class. ### write() -Writes a value to the servo, controlling the shaft accordingly. On a standard servo, this will set the angle of the shaft (in degrees), moving the shaft to that orientation. On a continuous rotation servo, this will set the speed of the servo (with 0 being full-speed in one direction, 180 being full speed in the other, and a value near 90 being no movement). +Write data to the last client that connected to a server. This data is sent as a byte or series of bytes. -```cpp +```C++ // SYNTAX -servo.write(angle) +server.write(val); +server.write(buf, len); ``` -### writeMicroseconds() +Parameters: -Writes a value in microseconds (uS) to the servo, controlling the shaft accordingly. On a standard servo, this will set the angle of the shaft. On standard servos a parameter value of 1000 is fully counter-clockwise, 2000 is fully clockwise, and 1500 is in the middle. +- `val`: a value to send as a single byte (byte or char) +- `buf`: an array to send as a series of bytes (byte or char) +- `len`: the length of the buffer -```cpp +Returns: `byte`: `write()` returns the number of bytes written. It is not necessary to read this. + +### print() + +Print data to the last client connected to a server. Prints numbers as a sequence of digits, each an ASCII character (e.g. the number 123 is sent as the three characters '1', '2', '3'). + +```C++ // SYNTAX -servo.writeMicroseconds(uS) +server.print(data); +server.print(data, BASE) ; ``` -Note that some manufactures do not follow this standard very closely so that servos often respond to values between 700 and 2300. Feel free to increase these endpoints until the servo no longer continues to increase its range. Note however that attempting to drive a servo past its endpoints (often indicated by a growling sound) is a high-current state, and should be avoided. +Parameters: -Continuous-rotation servos will respond to the writeMicrosecond function in an analogous manner to the write function. +- `data`: the data to print (char, byte, int, long, or string) +- `BASE`(optional): the base in which to print numbers: BIN for binary (base 2), DEC for decimal (base 10), OCT for octal (base 8), HEX for hexadecimal (base 16). +Returns: `byte`: `print()` will return the number of bytes written, though reading that number is optional -### read() +### println() -Read the current angle of the servo (the value passed to the last call to write()). Returns an integer from 0 to 180 degrees. +Print data, followed by a newline, to the last client connected to a server. Prints numbers as a sequence of digits, each an ASCII character (e.g. the number 123 is sent as the three characters '1', '2', '3'). -```cpp +```C++ // SYNTAX -servo.read() +server.println(); +server.println(data); +server.println(data, BASE) ; ``` -### attached() +Parameters: -Check whether the Servo variable is attached to a pin. Returns a boolean. +- `data` (optional): the data to print (char, byte, int, long, or string) +- `BASE` (optional): the base in which to print numbers: BIN for binary (base 2), DEC for decimal (base 10), OCT for octal (base 8), HEX for hexadecimal (base 16). -```cpp -// SYNTAX -servo.attached() -``` -### detach() +## TCPClient -Detach the Servo variable from its pin. +Creates a client which can connect to a specified internet IP address and port (defined in the `client.connect()` function). -```cpp +```C++ // SYNTAX -servo.detach() +TCPClient client; ``` -### setTrim() +```C++ +// EXAMPLE USAGE -Sets a trim value that allows minute timing adjustments to correctly -calibrate 90 as the stationary point. +TCPClient client; +byte server[] = { 74, 125, 224, 72 }; // Google +void setup() +{ + // Make sure your Serial Terminal app is closed before powering your device + Serial.begin(9600); + // Now open your Serial Terminal, and hit any key to continue! + while(!Serial.available()) Particle.process(); -```cpp -// SYNTAX + Serial.println("connecting..."); -// shortens the pulses sent to the servo -servo.setTrim(-3); + if (client.connect(server, 80)) + { + Serial.println("connected"); + client.println("GET /search?q=unicorn HTTP/1.0"); + client.println("Host: www.google.com"); + client.println("Content-Length: 0"); + client.println(); + } + else + { + Serial.println("connection failed"); + } +} -// a larger trim value -servo.setTrim(30); +void loop() +{ + if (client.available()) + { + char c = client.read(); + Serial.print(c); + } -// removes any previously configured trim -servo.setTrim(0); + if (!client.connected()) + { + Serial.println(); + Serial.println("disconnecting."); + client.stop(); + for(;;); + } +} ``` +### connected() -## RGB - -This library allows the user to control the RGB LED on the front of the device. +Whether or not the client is connected. Note that a client is considered connected if the connection has been closed but there is still unread data. -```cpp -// EXAMPLE CODE +```C++ +// SYNTAX +client.connected(); +``` -// take control of the LED -RGB.control(true); +Returns true if the client is connected, false if not. -// red, green, blue, 0-255. -// the following sets the RGB LED to white: -RGB.color(255, 255, 255); +### connect() -// wait one second -delay(1000); +Connects to a specified IP address and port. The return value indicates success or failure. Also supports DNS lookups when using a domain name. -// scales brightness of all three colors, 0-255. -// the following sets the RGB LED brightness to 25%: -RGB.brightness(64); +```C++ +// SYNTAX +client.connect(); +client.connect(ip, port); +client.connect(hostname, port); +``` -// wait one more second -delay(1000); +Parameters: -// resume normal operation -RGB.control(false); -``` +- `ip`: the IP address that the client will connect to (array of 4 bytes) +- `hostname`: the host name the client will connect to (string, ex.:"particle.io") +- `port`: the port that the client will connect to (`int`) -### control(user_control) +Returns true if the connection succeeds, false if not. -User can take control of the RGB LED, or give control back to the system. +### write() -```cpp -// take control of the RGB LED -RGB.control(true); +Write data to the server the client is connected to. This data is sent as a byte or series of bytes. -// resume normal operation -RGB.control(false); +```C++ +// SYNTAX +client.write(val); +client.write(buf, len); ``` -### controlled() +Parameters: -Returns Boolean `true` when the RGB LED is under user control, or `false` when it is not. +- `val`: a value to send as a single byte (byte or char) +- `buf`: an array to send as a series of bytes (byte or char) +- `len`: the length of the buffer -```cpp -// take control of the RGB LED -RGB.control(true); +Returns: `byte`: `write()` returns the number of bytes written. It is not necessary to read this value. -// Print true or false depending on whether -// the RGB LED is currently under user control. -// In this case it prints "true". -Serial.println(RGB.controlled()); +### print() -// resume normal operation -RGB.control(false); +Print data to the server that a client is connected to. Prints numbers as a sequence of digits, each an ASCII character (e.g. the number 123 is sent as the three characters '1', '2', '3'). + +```C++ +// SYNTAX +client.print(data); +client.print(data, BASE) ; ``` -### color(red, green, blue) +Parameters: -Set the color of the RGB with three values, 0 to 255 (0 is off, 255 is maximum brightness for that color). User must take control of the RGB LED before calling this method. +- `data`: the data to print (char, byte, int, long, or string) +- `BASE`(optional): the base in which to print numbers: BIN for binary (base 2), DEC for decimal (base 10), OCT for octal (base 8), HEX for hexadecimal (base 16). -```cpp -// Set the RGB LED to red -RGB.color(255, 0, 0); +Returns: `byte`: `print()` will return the number of bytes written, though reading that number is optional -// Sets the RGB LED to cyan -RGB.color(0, 255, 255); +### println() -// Sets the RGB LED to white -RGB.color(255, 255, 255); +Print data, followed by a carriage return and newline, to the server a client is connected to. Prints numbers as a sequence of digits, each an ASCII character (e.g. the number 123 is sent as the three characters '1', '2', '3'). + +```C++ +// SYNTAX +client.println(); +client.println(data); +client.println(data, BASE) ; ``` -### brightness(val) +Parameters: -Scale the brightness value of all three RGB colors with one value, 0 to 255 (0 is 0%, 255 is 100%). This setting persists after `RGB.control()` is set to `false`, and will govern the overall brightness of the RGB LED under normal system operation. User must take control of the RGB LED before calling this method. +- `data` (optional): the data to print (char, byte, int, long, or string) +- `BASE` (optional): the base in which to print numbers: BIN for binary (base 2), DEC for decimal (base 10), OCT for octal (base 8), HEX for hexadecimal (base 16). -```cpp -// Scale the RGB LED brightness to 25% -RGB.brightness(64); +### available() -// Scale the RGB LED brightness to 50% -RGB.brightness(128); +Returns the number of bytes available for reading (that is, the amount of data that has been written to the client by the server it is connected to). -// Scale the RGB LED brightness to 100% -RGB.brightness(255); +```C++ +// SYNTAX +client.available(); ``` -### onChange(handler) +Returns the number of bytes available. -Specifies a function to call when the color of the RGB LED changes. It can be used to implement an external RGB LED. +### read() +Read the next byte received from the server the client is connected to (after the last call to `read()`). -```cpp -// EXAMPLE USAGE +```C++ +// SYNTAX +client.read(); +``` -void ledChangeHandler(uint8_t r, uint8_t g, uint8_t b) { - // Duplicate the green color to an external LED - analogWrite(D0, g); -} +Returns the next byte (or character), or -1 if none is available. -void setup() -{ - pinMode(D0, OUTPUT); - RGB.onChange(ledChangeHandler); -} +or `int read(uint8_t *buffer, size_t size)` reads all readily available bytes up to `size` from the server the client is connected to into the provided `buffer`. +```C++ +// SYNTAX +bytesRead = client.read(buffer, length); ``` ---- +Returns the number of bytes (or characters) read into `buffer`. -`onChange` can also call a method on an object. +### flush() +Waits until all outgoing data in buffer has been sent. + +**NOTE:** That this function does nothing at present. + +```C++ +// SYNTAX +client.flush(); ``` -// Automatically mirror the onboard RGB LED to an external RGB LED -// No additional code needed in setup() or loop() -class ExternalRGB { - public: - ExternalRGB(pin_t r, pin_t g, pin_t b) : pin_r(r), pin_g(g), pin_b(b) { - pinMode(pin_r, OUTPUT); - pinMode(pin_g, OUTPUT); - pinMode(pin_b, OUTPUT); - RGB.onChange(&ExternalRGB::handler, this); - } - - void handler(uint8_t r, uint8_t g, uint8_t b) { - analogWrite(pin_r, 255 - r); - analogWrite(pin_g, 255 - g); - analogWrite(pin_b, 255 - b); - } - - private: - pin_t pin_r; - pin_t pin_g; - pin_t pin_b; -}; - -// Connect an external RGB LED to D0, D1 and D2 (R, G, and B) -ExternalRGB myRGB(D0, D1, D2); -``` +### remoteIP() +_Since 0.4.5_ -## Time +Retrieves the remote `IPAddress` of a connected `TCPClient`. When the `TCPClient` is retrieved +from `TCPServer.available()` (where the client is a remote client connecting to a local server) the +`IPAddress` gives the remote address of the connecting client. -The device synchronizes time with the Particle Cloud during the handshake. -From then, the time is continually updated on the device. -This reduces the need for external libraries to manage dates and times. +When `TCPClient` was created directly via `TCPClient.connect()`, then `remoteIP` +returns the remote server the client is connected to. +```C++ -### hour() +// EXAMPLE - TCPClient from TCPServer -Retrieve the hour for the current or given time. -Integer is returned without a leading zero. +TCPServer server(80); +// ... -```cpp -// Print the hour for the current time -Serial.print(Time.hour()); +void setup() +{ + Serial.begin(9600); + server.begin(80); +} -// Print the hour for the given time, in this case: 4 -Serial.print(Time.hour(1400647897)); +void loop() +{ + // check for a new client to our server + TCPClient client = server.available(); + if (client.connected()) + { + // we got a new client + // find where the client's remote address + IPAddress clientIP = client.remoteIP(); + // print the address to Serial + Serial.println(clientIP); + } +} ``` -Optional parameters: Integer (Unix timestamp) +```C++ +// EXAMPLE - TCPClient.connect() -Returns: Integer 0-23 +TCPClient client; +client.connect("www.google.com", 80); +if (client.connected()) +{ + IPAddress clientIP = client.remoteIP(); + // IPAddress equals whatever www.google.com resolves to +} +``` -### hourFormat12() -Retrieve the hour in 12-hour format for the current or given time. -Integer is returned without a leading zero. +### stop() -```cpp -// Print the hour in 12-hour format for the current time -Serial.print(Time.hourFormat12()); +Disconnect from the server. -// Print the hour in 12-hour format for the given time, in this case: 15 -Serial.print(Time.hourFormat12(1400684400)); +```C++ +// SYNTAX +client.stop(); ``` -Optional parameters: Integer (Unix timestamp) - -Returns: Integer 1-12 - -### isAM() +## UDP -Returns true if the current or given time is AM. +This class enables UDP messages to be sent and received. ```cpp -// Print true or false depending on whether the current time is AM -Serial.print(Time.isAM()); +// EXAMPLE USAGE -// Print whether the given time is AM, in this case: true -Serial.print(Time.isAM(1400647897)); -``` +// UDP Port used for two way communication +unsigned int localPort = 8888; -Optional parameters: Integer (Unix timestamp) +// An UDP instance to let us send and receive packets over UDP +UDP Udp; -Returns: Unsigned 8-bit integer: 0 = false, 1 = true +void setup() { + // start the UDP + Udp.begin(localPort); + // Print your device IP Address via serial + Serial.begin(9600); + Serial.println(WiFi.localIP()); +} -### isPM() +void loop() { + // Check if data has been received + if (Udp.parsePacket() > 0) { -Returns true if the current or given time is PM. + // Read first char of data received + char c = Udp.read(); -```cpp -// Print true or false depending on whether the current time is PM -Serial.print(Time.isPM()); + // Ignore other chars + while(Udp.available()) + Udp.read(); -// Print whether the given time is PM, in this case: false -Serial.print(Time.isPM(1400647897)); + // Store sender ip and port + IPAddress ipAddress = Udp.remoteIP(); + int port = Udp.remotePort(); + + // Echo back data to sender + Udp.beginPacket(ipAddress, port); + Udp.write(c); + Udp.endPacket(); + } +} ``` -Optional parameters: Integer (Unix timestamp) +_Note that UDP does not guarantee that messages are always delivered, or that +they are delivered in the order supplied. In cases where your application +requires a reliable connection, `TCPClient` is a simpler alternative._ -Returns: Unsigned 8-bit integer: 0 = false, 1 = true +{{#if core}} +The UDP protocol implementation has known issues that will require extra consideration when programming with it. Please refer to the Known Issues category of the Community for details. The are also numerous working examples and workarounds in the searchable Community topics. +{{/if}} +There are two primary ways of working with UDP - buffered operation and unbuffered operation. -### minute() +1. buffered operation allows you to read and write packets in small pieces, since the system takes care of allocating the required buffer to hold the entire packet. + - to read a buffered packet, call `parsePacket`, then use `available` and `read` to retrieve the packet received + - to write a buffered packet, optionally call `setBuffer` to set the maximum size of the packet (the default is 512 bytes), followed by + `beginPacket`, then as many calls to `write`/`print` as necessary to build the packet contents, followed finally by `end` to send the packet over the network. -Retrieve the minute for the current or given time. -Integer is returned without a leading zero. +2. unbuffered operation allows you to read and write entire packets in a single operation - your application is responsible for allocating the buffer to contain the packet to be sent or received over the network. + - to read an unbuffered packet, call `receivePacket` with a buffer to hold the received packet. + - to write an unbuffered packet, call `sendPacket` with the packet buffer to send, and the destination address. -```cpp -// Print the minute for the current time -Serial.print(Time.minute()); -// Print the minute for the given time, in this case: 51 -Serial.print(Time.minute(1400647897)); -``` + + -Optional parameters: Integer (Unix timestamp) +### begin() -Returns: Integer 0-59 +Initializes the UDP library and network settings. +```cpp +// SYNTAX +Udp.begin(port); +``` -### second() +### available() -Retrieve the seconds for the current or given time. -Integer is returned without a leading zero. +Get the number of bytes (characters) available for reading from the buffer. This is data that's already arrived. ```cpp -// Print the second for the current time -Serial.print(Time.second()); - -// Print the second for the given time, in this case: 51 -Serial.print(Time.second(1400647897)); +// SYNTAX +int count = Udp.available(); ``` -Optional parameters: Integer (Unix timestamp) +This function can only be successfully called after `UDP.parsePacket()`. -Returns: Integer 0-59 +`available()` inherits from the `Stream` utility class. +Returns the number of bytes available to read. -### day() +### beginPacket() -Retrieve the day for the current or given time. -Integer is returned without a leading zero. +Starts a connection to write UDP data to the remote connection. ```cpp -// Print the day for the current time -Serial.print(Time.day()); - -// Print the minute for the given time, in this case: 21 -Serial.print(Time.day(1400647897)); +// SYNTAX +Udp.beginPacket(remoteIP, remotePort); ``` -Optional parameters: Integer (Unix timestamp) +Parameters: -Returns: Integer 1-31 + - `remoteIP`: the IP address of the remote connection (4 bytes) + - `remotePort`: the port of the remote connection (int) +It returns nothing. -### weekday() +### endPacket() -Retrieve the weekday for the current or given time. +Called after writing buffered UDP data using `write()` or `print()`. The buffered data is then sent to the +remote UDP peer. - - 1 = Sunday - - 2 = Monday - - 3 = Tuesday - - 4 = Wednesday - - 5 = Thursday - - 6 = Friday - - 7 = Saturday ```cpp -// Print the weekday number for the current time -Serial.print(Time.weekday()); - -// Print the weekday for the given time, in this case: 4 -Serial.print(Time.weekday(1400647897)); +// SYNTAX +Udp.endPacket(); ``` -Optional parameters: Integer (Unix timestamp) - -Returns: Integer 1-7 - +Parameters: NONE -### month() +### write() -Retrieve the month for the current or given time. -Integer is returned without a leading zero. +Writes UDP data to the buffer - no data is actually sent. Must be wrapped between `beginPacket()` and `endPacket()`. `beginPacket()` initializes the packet of data, it is not sent until `endPacket()` is called. ```cpp -// Print the month number for the current time -Serial.print(Time.month()); - -// Print the month for the given time, in this case: 5 -Serial.print(Time.month(1400647897)); +// SYNTAX +Udp.write(message); +Udp.write(buffer, size); ``` -Optional parameters: Integer (Unix timestamp) +Parameters: -Returns: Integer 1-12 + - `message`: the outgoing message (char) + - `buffer`: an array to send as a series of bytes (byte or char) + - `size`: the length of the buffer +Returns: -### year() + - `byte`: returns the number of characters sent. This does not have to be read -Retrieve the 4-digit year for the current or given time. -```cpp -// Print the current year -Serial.print(Time.year()); +### parsePacket() -// Print the year for the given time, in this case: 2014 -Serial.print(Time.year(1400647897)); -``` - -Optional parameters: Integer (Unix timestamp) - -Returns: Integer - - -### now() - -Retrieve the current time as seconds since January 1, 1970 (commonly known as "Unix time" or "epoch time"). This time is not affected by the timezone setting. +Checks for the presence of a UDP packet, and reports the size. `parsePacket()` must be called before reading the buffer with `UDP.read()`. ```cpp -// Print the current Unix timestamp -Serial.print(Time.now()); // 1400647897 +// SYNTAX +size = Udp.parsePacket(); ``` -Returns: Integer - -### local() - -Retrieve the current time in the configured timezone as seconds since January 1, 1970 (commonly known as "Unix time" or "epoch time"). This time is affected by the timezone setting. +Parameters: NONE -Note that the functions in the `Time` class expect times in UTC time, so the result from this should be used carefully. +Returns: + - `int`: the size of a received UDP packet -### zone() +### read() -Set the time zone offset (+/-) from UTC. -The device will remember this offset until reboot. +Reads UDP data from the specified buffer. If no arguments are given, it will return the next character in the buffer. -*NOTE*: This function does not observe daylight savings time. +This function can only be successfully called after `UDP.parsePacket()`. ```cpp -// Set time zone to Eastern USA daylight saving time -Time.zone(-4); +// SYNTAX +count = Udp.read(); +count = Udp.read(packetBuffer, MaxSize); ``` +Parameters: -Parameters: floating point offset from UTC in hours, from -12.0 to 13.0 + - `packetBuffer`: buffer to hold incoming packets (char) + - `MaxSize`: maximum size of the buffer (int) +Returns: -### setTime() + - `int`: returns the character in the buffer or -1 if no character is available -Set the system time to the given timestamp. +### flush() -*NOTE*: This will override the time set by the Particle Cloud. -If the cloud connection drops, the reconnection handshake will set the time again +Waits until all outgoing data in buffer has been sent. -Also see: [`Particle.syncTime()`](#particle-synctime-) +**NOTE:** That this function does nothing at present. -```cpp -// Set the time to 2014-10-11 13:37:42 -Time.setTime(1413034662); +```C++ +// SYNTAX +Udp.flush(); ``` -Parameters: Unix timestamp (integer) - +### stop() -### timeStr() +Disconnect from the server. Release any resource being used during the UDP session. -Return string representation for the given time. ```cpp -Serial.print(Time.timeStr()); // Wed May 21 01:08:47 2014 +// SYNTAX +Udp.stop(); ``` -Returns: String - -_NB: In 0.3.4 and earlier, this function included a newline at the end of the returned string. This has been removed in 0.4.0._ +Parameters: NONE -### format() +### remoteIP() -Formats a time string using a configurable format. +Returns the IP address of sender of the packet parsed by `Udp.parsePacket()`/`Udp.receivePacket()`. ```cpp -// EXAMPLE - -time_t time = Time.now(); -Time.format(time, TIME_FORMAT_DEFAULT); // Sat Jan 10 08:22:04 2004 , same as Time.timeStr() - -Time.zone(-5.25); // setup a time zone, which is part of the ISO6801 format -Time.format(time, TIME_FORMAT_ISO8601_FULL); // 2004-01-10T08:22:04-05:15 - +// SYNTAX +ip = Udp.remoteIP(); ``` -The formats available are: +Parameters: NONE -- `TIME_FORMAT_DEFAULT` -- `TIME_FORMAT_ISO8601_FULL` -- custom format based on `strftime()` +Returns: -{{#if core}} -Note that the format function is implemented using `strftime()` which adds several kilobytes to the size of firmware. -Application firmware that has limited space available may want to consider using simpler alternatives that consume less firmware space, such as `sprintf()`. -{{/if}} + - IPAddress : the IP address of the sender of the packet parsed by `Udp.parsePacket()`/`Udp.receivePacket()`. -### setFormat() +### remotePort() -Sets the format string that is the default value used by `format()`. +Returns the port from which the UDP packet was sent. The packet is the one most recently processed by `Udp.parsePacket()`/`Udp.receivePacket()`. ```cpp - -Time.setFormat(TIME_FORMAT_ISO8601_FULL); - -``` - -In more advanced cases, you can set the format to a static string that follows -the same syntax as the `strftime()` function. - +// SYNTAX +int port = Udp.remotePort(); ``` -// custom formatting - -Time.format(Time.now(), "Now it's %I:%M%p."); -// Now it's 03:21AM. -``` +Parameters: NONE -### getFormat() +Returns: -Retrieves the currently configured format string for time formatting with `format()`. +- `int`: the port from which the packet parsed by `Udp.parsePacket()`/`Udp.receivePacket()` was sent. -### millis() +### setBuffer() -Returns the number of milliseconds since the device began running the current program. This number will overflow (go back to zero), after approximately 49 days. +_Since 0.4.5_ -`unsigned long time = millis();` +Initializes the buffer used by a `UDP` instance for buffered reads/writes. The buffer +is used when your application calls `beginPacket()` and `parsePacket()`. If `setBuffer()` isn't called, +the buffer size defaults to 512 bytes, and is allocated when buffered operation is initialized via `beginPacket()` or `parsePacket()`. -```C++ -// EXAMPLE USAGE +```cpp +// SYNTAX +Udp.setBuffer(size); // dynamically allocated buffer +Udp.setBuffer(size, buffer); // application provided buffer -unsigned long time; +// EXAMPLE USAGE - dynamically allocated buffer +UDP Udp; -void setup() +// uses a dynamically allocated buffer that is 1024 bytes in size +if (!Udp.setBuffer(1024)) { - Serial.begin(9600); + // on no, couldn't allocate the buffer } -void loop() +else { - Serial.print("Time: "); - time = millis(); - //prints time since program started - Serial.println(time); - // wait a second so as not to send massive amounts of data - delay(1000); + // 'tis good! } ``` -**Note:** -The return value for millis is an unsigned long, errors may be generated if a programmer tries to do math with other datatypes such as ints. -### micros() +```cpp +// EXAMPLE USAGE - application-provided buffer +UDP Udp; -Returns the number of microseconds since the device began running the current program. +char appBuffer[800]; +Udp.setBuffer(800, appBuffer); +``` -Firmware v0.4.3 and earlier: -- This number will overflow (go back to zero), after exactly 59,652,323 microseconds (0 .. 59,652,322) on the Core and after exactly 35,791,394 microseconds (0 .. 35,791,394) on the Photon and Electron. +Parameters: +- `unsigned int`: the size of the buffer +- `pointer`: the buffer. If not provided, or `NULL` the system will attempt to + allocate a buffer of the size requested. +Returns: +- `true` when the buffer was successfully allocated, `false` if there was insufficient memory. (For application-provided buffers +the function always returns `true`.) -`unsigned long time = micros();` +### releaseBuffer() -```C++ -// EXAMPLE USAGE +_Since 0.4.5_ -unsigned long time; +Releases the buffer previously set by a call to `setBuffer()`. -void setup() -{ - Serial.begin(9600); -} -void loop() -{ - Serial.print("Time: "); - time = micros(); - //prints time since program started - Serial.println(time); - // wait a second so as not to send massive amounts of data - delay(1000); -} +```cpp +// SYNTAX +Udp.releaseBuffer(); ``` -### delay() +_This is typically required only when performing advanced memory management and the UDP instance is +not scoped to the lifetime of the application._ -Pauses the program for the amount of time (in miliseconds) specified as parameter. (There are 1000 milliseconds in a second.) +### sendPacket() -```C++ -// SYNTAX -delay(ms); -``` +_Since 0.4.5_ -`ms` is the number of milliseconds to pause *(unsigned long)* +Sends a packet, unbuffered, to a remote UDP peer. + +```cpp +// SYNTAX +Udp.sendPacket(buffer, bufferSize, remoteIP, remotePort); -```C++ // EXAMPLE USAGE +UDP Udp; -int ledPin = D1; // LED connected to digital pin D1 +char buffer[] = "Particle powered"; -void setup() -{ - pinMode(ledPin, OUTPUT); // sets the digital pin as output -} +IPAddress remoteIP(192, 168, 1, 100); +int port = 1337; -void loop() -{ - digitalWrite(ledPin, HIGH); // sets the LED on - delay(1000); // waits for a second - digitalWrite(ledPin, LOW); // sets the LED off - delay(1000); // waits for a second +void setup() { + // Required for two way communication + Udp.begin(8888); + + if (Udp.sendPacket(buffer, sizeof(buffer), remoteIP, port) < 0) { + Particle.publish("Error"); + } } ``` -**NOTE:** -the parameter for millis is an unsigned long, errors may be generated if a programmer tries to do math with other datatypes such as ints. -### delayMicroseconds() +Parameters: +- `pointer` (buffer): the buffer of data to send +- `int` (bufferSize): the number of bytes of data to send +- `IPAddress` (remoteIP): the destination address of the remote peer +- `int` (remotePort): the destination port of the remote peer -Pauses the program for the amount of time (in microseconds) specified as parameter. There are a thousand microseconds in a millisecond, and a million microseconds in a second. +Returns: +- `int`: The number of bytes written. Negative value on error. -```C++ +{{#if photon}} +### joinMulticast() + +_Since 0.4.5_ + +Join a multicast address for all UDP sockets which are on the same network interface as this one. + +```cpp // SYNTAX -delayMicroseconds(us); -``` -`us` is the number of microseconds to pause *(unsigned int)* +Udp.joinMulticast(IPAddress& ip); -```C++ // EXAMPLE USAGE +UDP Udp; -int outPin = D1; // digital pin D1 +int remotePort = 1024; +IPAddress multicastAddress(224,0,0,0); -void setup() -{ - pinMode(outPin, OUTPUT); // sets the digital pin as output -} +Udp.begin(remotePort); +Udp.joinMulticast(multicastAddress); +``` -void loop() -{ - digitalWrite(outPin, HIGH); // sets the pin on - delayMicroseconds(50); // pauses for 50 microseconds - digitalWrite(outPin, LOW); // sets the pin off - delayMicroseconds(50); // pauses for 50 microseconds -} -``` - -## Interrupts - -Interrupts are a way to write code that is run when an external event occurs. -As a general rule, interrupt code should be very fast, and non-blocking. This means -performing transfers, such as I2C, Serial, TCP should not be done as part of the -interrupt handler. Rather, the interrupt handler can set a variable which instructs -the main loop that the event has occurred. - -### attachInterrupt() - -Specifies a function to call when an external interrupt occurs. Replaces any previous function that was attached to the interrupt. +This will allow reception of multicast packets sent to the given address for UDP sockets +which have bound the port to which the multicast packet was sent. +Must be called only after `begin()` so that the network interface is established. -**NOTE:** -`pinMode()` MUST be called prior to calling attachInterrupt() to set the desired mode for the interrupt pin (INPUT, INPUT_PULLUP or INPUT_PULLDOWN). +### leaveMulticast() -External interrupts are supported on the following pins: +_Since 0.4.5_ -- Core: D0, D1, D2, D3, D4, A0, A1, A3, A4, A5, A6, A7 -- Photon: All pins with the exception of D0 and A5 (since at present Mode Button external interrupt(EXTI) line is shared with D0, A5). Also please note following are the pins for which EXTI lines are shared so only one can work at a time: - - D1, A4 - - D2, A0, A3 - - D3, DAC - - D4, A1 +Leaves a multicast group previously joined on a specific multicast address. -``` +```cpp // SYNTAX -attachInterrupt(pin, function, mode); -attachInterrupt(pin, function, mode, priority); -attachInterrupt(pin, function, mode, priority, subpriority); +Udp.leaveMulticast(multicastAddress); + +// EXAMPLE USAGE +UDP Udp; +IPAddress multicastAddress(224,0,0,0); +Udp.leaveMulticast(multicastAddress); ``` -*Parameters:* +{{/if}} {{!-- photon --}} -- `pin`: the pin number -- `function`: the function to call when the interrupt occurs; this function must take no parameters and return nothing. This function is sometimes referred to as an *interrupt service routine* (ISR). -- `mode`: defines when the interrupt should be triggered. Three constants are predefined as valid values: - - CHANGE to trigger the interrupt whenever the pin changes value, - - RISING to trigger when the pin goes from low to high, - - FALLING for when the pin goes from high to low. -- `priority` (optional): the priority of this interrupt. Default priority is 13. Lower values increase the priority of the interrupt. -- `subpriority` (optional): the subpriority of this interrupt. Default subpriority is 0. +## Servo -The function returns a boolaen whether the ISR was successfully attached (true) or not (false). +This library allows your device to control RC (hobby) servo motors. Servos have integrated gears and a shaft that can be precisely controlled. Standard servos allow the shaft to be positioned at various angles, usually between 0 and 180 degrees. Continuous rotation servos allow the rotation of the shaft to be set to various speeds. -```C++ -// EXAMPLE USAGE +```cpp +// EXAMPLE CODE -void blink(void); -int ledPin = D1; -volatile int state = LOW; +Servo myservo; // create servo object to control a servo + // a maximum of eight servo objects can be created + +int pos = 0; // variable to store the servo position void setup() { - pinMode(ledPin, OUTPUT); - pinMode(D2, INPUT_PULLUP); - attachInterrupt(D2, blink, CHANGE); + myservo.attach(D0); // attaches the servo on the D0 pin to the servo object + // Only supported on pins that have PWM } -void loop() -{ - digitalWrite(ledPin, state); -} -void blink() +void loop() { - state = !state; + for(pos = 0; pos < 180; pos += 1) // goes from 0 degrees to 180 degrees + { // in steps of 1 degree + myservo.write(pos); // tell servo to go to position in variable 'pos' + delay(15); // waits 15ms for the servo to reach the position + } + for(pos = 180; pos>=1; pos-=1) // goes from 180 degrees to 0 degrees + { + myservo.write(pos); // tell servo to go to position in variable 'pos' + delay(15); // waits 15ms for the servo to reach the position + } } ``` -You can attach a method in a C++ object as an interrupt handler. +**NOTE:** Unlike Arduino, you do not need to include `Servo.h`; it is included automatically. -```cpp -class Robot { - public: - Robot() { - pinMode(D2, INPUT_PULLUP); - attachInterrupt(D2, &Robot::handler, this, CHANGE); - } - void handler() { - // do something on interrupt - } -}; -Robot myRobot; -// nothing else needed in setup() or loop() -``` +### attach() -**Using Interrupts:** -Interrupts are useful for making things happen automatically in microcontroller programs, and can help solve timing problems. Good tasks for using an interrupt may include reading a rotary encoder, or monitoring user input. +Set up a servo on a particular pin. Note that, Servo can only be attached to pins with a timer. -If you wanted to insure that a program always caught the pulses from a rotary encoder, so that it never misses a pulse, it would make it very tricky to write a program to do anything else, because the program would need to constantly poll the sensor lines for the encoder, in order to catch pulses when they occurred. Other sensors have a similar interface dynamic too, such as trying to read a sound sensor that is trying to catch a click, or an infrared slot sensor (photo-interrupter) trying to catch a coin drop. In all of these situations, using an interrupt can free the microcontroller to get some other work done while not missing the input. +- on the Core, Servo can be connected to A0, A1, A4, A5, A6, A7, D0, and D1. +- on the Photon, Servo can be connected to A4, A5, WKP, RX, TX, D0, D1, D2, D3 -**About Interrupt Service Routines:** -ISRs are special kinds of functions that have some unique limitations most other functions do not have. An ISR cannot have any parameters, and they shouldn't return anything. +```cpp +// SYNTAX +servo.attach(pin) +``` -Generally, an ISR should be as short and fast as possible. If your sketch uses multiple ISRs, only one can run at a time, other interrupts will be executed after the current one finishes in an order that depends on the priority they have. `millis()` relies on interrupts to count, so it will never increment inside an ISR. Since `delay()` requires interrupts to work, it will not work if called inside an ISR. Using `delayMicroseconds()` will work as normal. +### write() -Typically global variables are used to pass data between an ISR and the main program. To make sure variables shared between an ISR and the main program are updated correctly, declare them as `volatile`. +Writes a value to the servo, controlling the shaft accordingly. On a standard servo, this will set the angle of the shaft (in degrees), moving the shaft to that orientation. On a continuous rotation servo, this will set the speed of the servo (with 0 being full-speed in one direction, 180 being full speed in the other, and a value near 90 being no movement). +```cpp +// SYNTAX +servo.write(angle) +``` -### detachInterrupt() +### writeMicroseconds() -Turns off the given interrupt. +Writes a value in microseconds (uS) to the servo, controlling the shaft accordingly. On a standard servo, this will set the angle of the shaft. On standard servos a parameter value of 1000 is fully counter-clockwise, 2000 is fully clockwise, and 1500 is in the middle. -``` +```cpp // SYNTAX -detachInterrupt(pin); +servo.writeMicroseconds(uS) ``` -`pin` is the pin number of the interrupt to disable. - +Note that some manufactures do not follow this standard very closely so that servos often respond to values between 700 and 2300. Feel free to increase these endpoints until the servo no longer continues to increase its range. Note however that attempting to drive a servo past its endpoints (often indicated by a growling sound) is a high-current state, and should be avoided. -### interrupts() +Continuous-rotation servos will respond to the writeMicrosecond function in an analogous manner to the write function. -Re-enables interrupts (after they've been disabled by `noInterrupts()`). Interrupts allow certain important tasks to happen in the background and are enabled by default. Some functions will not work while interrupts are disabled, and incoming communication may be ignored. Interrupts can slightly disrupt the timing of code, however, and may be disabled for particularly critical sections of code. -```C++ -// EXAMPLE USAGE +### read() -void setup() {} +Read the current angle of the servo (the value passed to the last call to write()). Returns an integer from 0 to 180 degrees. -void loop() -{ - noInterrupts(); // disable interrupts - // - // put critical, time-sensitive code here - // - interrupts(); // enable interrupts - // - // other code here - // -} +```cpp +// SYNTAX +servo.read() ``` -`interrupts()` neither accepts a parameter nor returns anything. - -### noInterrupts() +### attached() -Disables interrupts (you can re-enable them with `interrupts()`). Interrupts allow certain important tasks to happen in the background and are enabled by default. Some functions will not work while interrupts are disabled, and incoming communication may be ignored. Interrupts can slightly disrupt the timing of code, however, and may be disabled for particularly critical sections of code. +Check whether the Servo variable is attached to a pin. Returns a boolean. +```cpp // SYNTAX -noInterrupts(); - -`noInterrupts()` neither accepts a parameter nor returns anything. - -## Software Timers +servo.attached() +``` -_Since 0.4.7. This feature is available on the Photon, P1 and Electron out the box. On the Core, the -`freertos4core` library should be used to add FreeRTOS to the core._ +### detach() -Software Timers provide a way to have timed actions in your program. FreeRTOS provides the ability to have up to 10 Software Timers at a time with a minimum resolution of 1 millisecond. It is common to use millis() based "timers" though exact timing is not always possible (due to other program delays). Software timers are maintained by FreeRTOS and provide a more reliable method for running timed actions using callback functions. Please note that Software Timers are "chained" and will be serviced sequencially when several timers trigger simultaneously, thus requiring special consideration when writing callback functions. +Detach the Servo variable from its pin. ```cpp -// EXAMPLE - -void print_every_second() -{ - static int count = 0; - Serial.println(count++); -} - -Timer timer(1000, print_every_second); - -void setup() -{ - Serial.begin(9600); - timer.start(); -} +// SYNTAX +servo.detach() ``` -Timers may be started, stopped, reset within a user program or an ISR. They may also be "disposed", removing them from the (max. 10) active timer list. +### setTrim() -The timer callback is similar to an interrupt - it shouldn't block. However, it is less restrictive than an interrupt. If the code does block, the system will not crash - the only consequence is that other software timers that should have triggered will be delayed until the blocking timer callback function returns. +Sets a trim value that allows minute timing adjustments to correctly +calibrate 90 as the stationary point. +```cpp // SYNTAX -`Timer timer(period, callback, one_shot)` +// shortens the pulses sent to the servo +servo.setTrim(-3); -- `period` is the period of the timer in milliseconds (unsigned int) -- `callback` is the callback function which gets called when the timer expires. -- `one_shot` (optional, since 0.4.9) when `true`, the timer is fired once and then stopped automatically. The default is `false` - a repeating timer. +// a larger trim value +servo.setTrim(30); +// removes any previously configured trim +servo.setTrim(0); +``` -### Class member callbacks +{{#if has-rgb}} -_Since 0.4.9_ +## RGB -A class member function can be used as a callback using this syntax to create the timer: +This library allows the user to control the RGB LED on the front of the device. -`Timer timer(period, callback, instance, one_shot)` +```cpp +// EXAMPLE CODE -- `period` is the period of the timer in milliseconds (unsigned int) -- `callback` is the class member function which gets called when the timer expires. -- `instance` the instance of the class to call the callback function on. -- `one_shot` (optional, since 0.4.9) when `true`, the timer is fired once and then stopped automatically. The default is `false` - a repeating timer. +// take control of the LED +RGB.control(true); +// red, green, blue, 0-255. +// the following sets the RGB LED to white: +RGB.color(255, 255, 255); -``` -// Class member function callback example +// wait one second +delay(1000); -class CallbackClass -{ -public: - void onTimeout(); -} +// scales brightness of all three colors, 0-255. +// the following sets the RGB LED brightness to 25%: +RGB.brightness(64); -CallbackClass callback; -Timer t(1000, &CallbackClass::onTimeout, callback); +// wait one more second +delay(1000); +// resume normal operation +RGB.control(false); ``` +### control(user_control) -### start() +User can take control of the RGB LED, or give control back to the system. -Starts a stopped timer (a newly created timer is stopped). If `start()` is called for a running timer, it will be reset. +```cpp +// take control of the RGB LED +RGB.control(true); -`start()` +// resume normal operation +RGB.control(false); +``` -```C++ -// EXAMPLE USAGE -timer.start(); // starts timer if stopped or resets it if started. - -``` - -### stop() +### controlled() -Stops a running timer. +Returns Boolean `true` when the RGB LED is under user control, or `false` when it is not. -`stop()` +```cpp +// take control of the RGB LED +RGB.control(true); -```C++ -// EXAMPLE USAGE -timer.stop(); // stops a running timer. +// Print true or false depending on whether +// the RGB LED is currently under user control. +// In this case it prints "true". +Serial.println(RGB.controlled()); +// resume normal operation +RGB.control(false); ``` -### changePeriod() - -Changes the period of a previously created timer. It can be called to change the period of an running or stopped timer. +### color(red, green, blue) -`changePeriod(newPeriod)` +Set the color of the RGB with three values, 0 to 255 (0 is off, 255 is maximum brightness for that color). User must take control of the RGB LED before calling this method. -`newPeriod` is the new timer period (unsigned int) +```cpp +// Set the RGB LED to red +RGB.color(255, 0, 0); -```C++ -// EXAMPLE USAGE -timer.changePeriod(1000); // Reset period of timer to 1000ms. +// Sets the RGB LED to cyan +RGB.color(0, 255, 255); +// Sets the RGB LED to white +RGB.color(255, 255, 255); ``` +### brightness(val) -### reset() - -Resets a timer. If a timer is running, it will reset to "zero". If a timer is stopped, it will be started. +Scale the brightness value of all three RGB colors with one value, 0 to 255 (0 is 0%, 255 is 100%). This setting persists after `RGB.control()` is set to `false`, and will govern the overall brightness of the RGB LED under normal system operation. User must take control of the RGB LED before calling this method. -`reset()` +```cpp +// Scale the RGB LED brightness to 25% +RGB.brightness(64); -```C++ -// EXAMPLE USAGE -timer.reset(); // reset timer if running, or start timer if stopped. +// Scale the RGB LED brightness to 50% +RGB.brightness(128); +// Scale the RGB LED brightness to 100% +RGB.brightness(255); ``` -### startFromISR() -### stopFromISR() -### resetFromISR() -### changePeriodFromISR() - -`startFromISR()` -`stopFromISR()` -`resetFromISR()` -`changePeriodFromISR()` +### onChange(handler) -Start, stop and reset a timer or change a timer's period (as above) BUT from within an ISR. These functions MUST be called when doing timer operations within an ISR. +Specifies a function to call when the color of the RGB LED changes. It can be used to implement an external RGB LED. -```C++ +```cpp // EXAMPLE USAGE -timer.startFromISR(); // WITHIN an ISR, starts timer if stopped or resets it if started. -timer.stopFromISR(); // WITHIN an ISR,stops a running timer. +void ledChangeHandler(uint8_t r, uint8_t g, uint8_t b) { + // Duplicate the green color to an external LED + analogWrite(D0, g); +} -timer.resetFromISR(); // WITHIN an ISR, reset timer if running, or start timer if stopped. +void setup() +{ + pinMode(D0, OUTPUT); + RGB.onChange(ledChangeHandler); +} -timer.changePeriodFromISR(newPeriod); // WITHIN an ISR, change the timer period. ``` -### dispose() - -`dispose()` - -Stop and remove a timer from the (max. 10) timer list, freeing a timer "slot" in the list. +--- -```C++ -// EXAMPLE USAGE -timer.dispose(); // stop and delete timer from timer list. +`onChange` can also call a method on an object. ``` +// Automatically mirror the onboard RGB LED to an external RGB LED +// No additional code needed in setup() or loop() -### isActive() - -_Since 0.5.0_ +class ExternalRGB { + public: + ExternalRGB(pin_t r, pin_t g, pin_t b) : pin_r(r), pin_g(g), pin_b(b) { + pinMode(pin_r, OUTPUT); + pinMode(pin_g, OUTPUT); + pinMode(pin_b, OUTPUT); + RGB.onChange(&ExternalRGB::handler, this); + } -`bool isActive()` + void handler(uint8_t r, uint8_t g, uint8_t b) { + analogWrite(pin_r, 255 - r); + analogWrite(pin_g, 255 - g); + analogWrite(pin_b, 255 - b); + } -Returns `true` if the timer is in active state (pending), or `false` otherwise. + private: + pin_t pin_r; + pin_t pin_g; + pin_t pin_b; +}; -```C++ -// EXAMPLE USAGE -if (timer.isActive()) { - // ... -} +// Connect an external RGB LED to D0, D1 and D2 (R, G, and B) +ExternalRGB myRGB(D0, D1, D2); ``` -{{#unless core}} +{{/if}} {{!-- has-rgb --}} -## Application Watchdog +## Time -_Since 0.5.0_ +The device synchronizes time with the Particle Cloud during the handshake. +From then, the time is continually updated on the device. +This reduces the need for external libraries to manage dates and times. -The Application Watchdog is a software-implemented watchdog using a critical-priority thread that wakes up at a given timeout interval to see if the application has checked in. -If the application has not exited loop, or called Particle.process() within the given timeout, or called `ApplicationWatchdog.checkin()`, the watchdog calls the given timeout function, which is typically `System.reset`. This could also be a user defined function that takes care of critical tasks before finally calling `System.reset`. +### hour() +Retrieve the hour for the current or given time. +Integer is returned without a leading zero. ```cpp -// SYNTAX -// declare a global watchdog instance -ApplicationWatchdog wd(timeout_milli_seconds, timeout_function_to_call, stack_size); +// Print the hour for the current time +Serial.print(Time.hour()); -// default stack_size of 512 bytes is used -ApplicationWatchdog wd(timeout_milli_seconds, timeout_function_to_call); +// Print the hour for the given time, in this case: 4 +Serial.print(Time.hour(1400647897)); +``` -// EXAMPLE USAGE -// reset the system after 60 seconds if the application is unresponsive -ApplicationWatchdog wd(60000, System.reset); +Optional parameters: Integer (Unix timestamp) -void loop() { - while (some_long_process_within_loop) { - wd.checkin(); // resets the AWDT count - } -} -// AWDT count reset automatically after loop() ends -``` +Returns: Integer 0-23 -A default `stack_size` of 512 is used for the thread. `stack_size` is an optional parameter. The stack can be made larger or smaller as needed. This is the amount of memory needed to support the thread and function that is called. In practice, on the Photon (v0.5.0) calling the `System.reset` function requires approx. 170 bytes of memory. If not enough memory is allocated, the application will crash due to a Stack Overflow. The RGB LED will flash a [red SOS pattern, followed by 13 blinks](/guide/getting-started/modes/photon/#red-flash-sos). -The application watchdog requires interrupts to be active in order to function. Enabling the hardware watchdog in combination with this is recommended, so that the system resets in the event that interrupts are not firing. +### hourFormat12() -{{/unless}} +Retrieve the hour in 12-hour format for the current or given time. +Integer is returned without a leading zero. -## Math +```cpp +// Print the hour in 12-hour format for the current time +Serial.print(Time.hourFormat12()); -Note that in addition to functions outlined below all of the newlib math functions described at [sourceware.org](https://sourceware.org/newlib/libm.html) are also available for use by simply including the math.h header file thus: +// Print the hour in 12-hour format for the given time, in this case: 15 +Serial.print(Time.hourFormat12(1400684400)); +``` -`#include "math.h"` +Optional parameters: Integer (Unix timestamp) -### min() +Returns: Integer 1-12 -Calculates the minimum of two numbers. -`min(x, y)` +### isAM() -`x` is the first number, any data type -`y` is the second number, any data type +Returns true if the current or given time is AM. -The functions returns the smaller of the two numbers. +```cpp +// Print true or false depending on whether the current time is AM +Serial.print(Time.isAM()); -```C++ -// EXAMPLE USAGE -sensVal = min(sensVal, 100); // assigns sensVal to the smaller of sensVal or 100 - // ensuring that it never gets above 100. +// Print whether the given time is AM, in this case: true +Serial.print(Time.isAM(1400647897)); ``` -**NOTE:** -Perhaps counter-intuitively, max() is often used to constrain the lower end of a variable's range, while min() is used to constrain the upper end of the range. +Optional parameters: Integer (Unix timestamp) -**WARNING:** -Because of the way the min() function is implemented, avoid using other functions inside the brackets, it may lead to incorrect results +Returns: Unsigned 8-bit integer: 0 = false, 1 = true -```C++ -min(a++, 100); // avoid this - yields incorrect results -a++; -min(a, 100); // use this instead - keep other math outside the function -``` +### isPM() -### max() +Returns true if the current or given time is PM. -Calculates the maximum of two numbers. +```cpp +// Print true or false depending on whether the current time is PM +Serial.print(Time.isPM()); -`max(x, y)` +// Print whether the given time is PM, in this case: false +Serial.print(Time.isPM(1400647897)); +``` -`x` is the first number, any data type -`y` is the second number, any data type +Optional parameters: Integer (Unix timestamp) -The functions returns the larger of the two numbers. +Returns: Unsigned 8-bit integer: 0 = false, 1 = true -```C++ -// EXAMPLE USAGE -sensVal = max(senVal, 20); // assigns sensVal to the larger of sensVal or 20 - // (effectively ensuring that it is at least 20) -``` -**NOTE:** -Perhaps counter-intuitively, max() is often used to constrain the lower end of a variable's range, while min() is used to constrain the upper end of the range. +### minute() -**WARNING:** -Because of the way the max() function is implemented, avoid using other functions inside the brackets, it may lead to incorrect results +Retrieve the minute for the current or given time. +Integer is returned without a leading zero. -```C++ -max(a--, 0); // avoid this - yields incorrect results +```cpp +// Print the minute for the current time +Serial.print(Time.minute()); -a--; // use this instead - -max(a, 0); // keep other math outside the function +// Print the minute for the given time, in this case: 51 +Serial.print(Time.minute(1400647897)); ``` -### abs() - -Computes the absolute value of a number. +Optional parameters: Integer (Unix timestamp) -`abs(x);` +Returns: Integer 0-59 -where `x` is the number -The function returns `x` if `x` is greater than or equal to `0` -and returns `-x` if `x` is less than `0`. +### second() -**WARNING:** -Because of the way the abs() function is implemented, avoid using other functions inside the brackets, it may lead to incorrect results. +Retrieve the seconds for the current or given time. +Integer is returned without a leading zero. -```C++ -abs(a++); // avoid this - yields incorrect results +```cpp +// Print the second for the current time +Serial.print(Time.second()); -a++; // use this instead - -abs(a); // keep other math outside the function +// Print the second for the given time, in this case: 51 +Serial.print(Time.second(1400647897)); ``` -### constrain() +Optional parameters: Integer (Unix timestamp) -Constrains a number to be within a range. +Returns: Integer 0-59 -`constrain(x, a, b);` -`x` is the number to constrain, all data types -`a` is the lower end of the range, all data types -`b` is the upper end of the range, all data types +### day() -The function will return: -`x`: if x is between `a` and `b` -`a`: if `x` is less than `a` -`b`: if `x` is greater than `b` +Retrieve the day for the current or given time. +Integer is returned without a leading zero. -```C++ -// EXAMPLE USAGE -sensVal = constrain(sensVal, 10, 150); -// limits range of sensor values to between 10 and 150 +```cpp +// Print the day for the current time +Serial.print(Time.day()); + +// Print the minute for the given time, in this case: 21 +Serial.print(Time.day(1400647897)); ``` -### map() +Optional parameters: Integer (Unix timestamp) -```C++ -// EXAMPLE USAGE +Returns: Integer 1-31 -// Map an analog value to 8 bits (0 to 255) -void setup() { - pinMode(D1, OUTPUT); -} -void loop() -{ - int val = analogRead(A0); - val = map(val, 0, 4095, 0, 255); - analogWrite(D1, val); -} +### weekday() + +Retrieve the weekday for the current or given time. + + - 1 = Sunday + - 2 = Monday + - 3 = Tuesday + - 4 = Wednesday + - 5 = Thursday + - 6 = Friday + - 7 = Saturday + +```cpp +// Print the weekday number for the current time +Serial.print(Time.weekday()); + +// Print the weekday for the given time, in this case: 4 +Serial.print(Time.weekday(1400647897)); ``` -Re-maps a number from one range to another. That is, a value of fromLow would get mapped to `toLow`, a `value` of `fromHigh` to `toHigh`, values in-between to values in-between, etc. +Optional parameters: Integer (Unix timestamp) -`map(value, fromLow, fromHigh, toLow, toHigh);` +Returns: Integer 1-7 -Does not constrain values to within the range, because out-of-range values are sometimes intended and useful. The `constrain()` function may be used either before or after this function, if limits to the ranges are desired. -Note that the "lower bounds" of either range may be larger or smaller than the "upper bounds" so the `map()` function may be used to reverse a range of numbers, for example +### month() -`y = map(x, 1, 50, 50, 1);` +Retrieve the month for the current or given time. +Integer is returned without a leading zero. -The function also handles negative numbers well, so that this example +```cpp +// Print the month number for the current time +Serial.print(Time.month()); -`y = map(x, 1, 50, 50, -100);` +// Print the month for the given time, in this case: 5 +Serial.print(Time.month(1400647897)); +``` -is also valid and works well. +Optional parameters: Integer (Unix timestamp) -The `map()` function uses integer math so will not generate fractions, when the math might indicate that it should do so. Fractional remainders are truncated, and are not rounded or averaged. +Returns: Integer 1-12 -*Parameters:* -- `value`: the number to map -- `fromLow`: the lower bound of the value's current range -- `fromHigh`: the upper bound of the value's current range -- `toLow`: the lower bound of the value's target range -- `toHigh`: the upper bound of the value's target range +### year() -The function returns the mapped value +Retrieve the 4-digit year for the current or given time. -*Appendix:* -For the mathematically inclined, here's the whole function +```cpp +// Print the current year +Serial.print(Time.year()); -```C++ -long map(long x, long in_min, long in_max, long out_min, long out_max) -{ - return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; -} +// Print the year for the given time, in this case: 2014 +Serial.print(Time.year(1400647897)); ``` -### pow() +Optional parameters: Integer (Unix timestamp) -Calculates the value of a number raised to a power. `pow()` can be used to raise a number to a fractional power. This is useful for generating exponential mapping of values or curves. +Returns: Integer -`pow(base, exponent);` -`base` is the number *(float)* -`exponent` is the power to which the base is raised *(float)* +### now() -The function returns the result of the exponentiation *(double)* +Retrieve the current time as seconds since January 1, 1970 (commonly known as "Unix time" or "epoch time"). This time is not affected by the timezone setting. -EXAMPLE **TBD** +```cpp +// Print the current Unix timestamp +Serial.print(Time.now()); // 1400647897 +``` -### sqrt() +Returns: Integer -Calculates the square root of a number. +### local() -`sqrt(x)` +Retrieve the current time in the configured timezone as seconds since January 1, 1970 (commonly known as "Unix time" or "epoch time"). This time is affected by the timezone setting. -`x` is the number, any data type +Note that the functions in the `Time` class expect times in UTC time, so the result from this should be used carefully. -The function returns the number's square root *(double)* +_Since 0.6.0_ -## Random Numbers +Local time is also affected by the Daylight Saving Time (DST) settings. -The firmware incorporates a pseudo-random number generator. +### zone() -### random() +Set the time zone offset (+/-) from UTC. +The device will remember this offset until reboot. -Retrieves the next random value, restricted to a given range. +*NOTE*: This function does not observe daylight savings time. - `random(max);` +```cpp +// Set time zone to Eastern USA daylight saving time +Time.zone(-4); +``` -Parameters +Parameters: floating point offset from UTC in hours, from -12.0 to 14.0 -- `max` - the upper limit of the random number to retrieve. +### isDST() -Returns: a random value between 0 and up to, but not including `max`. +_Since 0.6.0_ -```c++ -int r = random(10); -// r is >= 0 and < 10 -// The smallest value returned is 0 -// The largest value returned is 9 +Returns true if Daylight Saving Time (DST) is in effect. + +```cpp +// Print true or false depending on whether the DST in in effect +Serial.print(Time.isDST()); ``` - NB: When `max` is 0, the result is always 0. +Returns: Unsigned 8-bit integer: 0 = false, 1 = true ---- +### getDSTOffset() -`random(min,max);` +_Since 0.6.0_ -Parameters: +Retrieve the current Daylight Saving Time (DST) offset that is added to the current local time when Time.beginDST() has been called. The default is 1 hour. - - `min` - the lower limit (inclusive) of the random number to retrieve. - - `max` - the upper limit (exclusive) of the random number to retrieve. +```cpp +// Get current DST offset +float offset = Time.getDSTOffset(); +``` -Returns: a random value from `min` and up to, but not including `max`. +Returns: floating point DST offset in hours (default is +1.0 hours) +### setDSTOffset() -```c++ -int r = random(10, 100); -// r is >= 10 and < 100 -// The smallest value returned is 10 -// The largest value returned is 99 -``` +_Since 0.6.0_ - NB: If `min` is greater or equal to `max`, the result is always 0. +Set a custom Daylight Saving Time (DST) offset. +The device will remember this offset until reboot. -### randomSeed() +```cpp +// Set DST offset to 30 minutes +Time.setDSTOffset(0.5); +``` -`randomSeed(newSeed);` +Parameters: floating point offset in hours, from 0.0 to 2.0 -Parameters: +### beginDST() - - `newSeed` - the new random seed +_Since 0.6.0_ -The pseudorandom numbers produced by the firmware are derived from a single value - the random seed. -The value of this seed fully determines the sequence of random numbers produced by successive -calls to `random()`. Using the same seed on two separate runs will produce -the same sequence of random numbers, and in contrast, using different seeds -will produce a different sequence of random numbers. +Start applying Daylight Saving Time (DST) offset to the current time. -On startup, the default random seed is [set by the system](http://www.cplusplus.com/reference/cstdlib/srand/) to 1. -Unless the seed is modified, the same sequence of random numbers would be produced each time -the system starts. +### endDST() -Fortunately, when the device connects to the cloud, it receives a very randomized seed value, -which is used as the random seed. So you can be sure the random numbers produced -will be different each time your program is run. +_Since 0.6.0_ +Stop applying Daylight Saving Time (DST) offset to the current time. -*** Disable random seed from the cloud *** +### setTime() -When the device receives a new random seed from the cloud, it's passed to this function: +Set the system time to the given timestamp. -``` -void random_seed_from_cloud(unsigned int seed); -``` +*NOTE*: This will override the time set by the Particle Cloud. +If the cloud connection drops, the reconnection handshake will set the time again -The system implementation of this function calls `randomSeed()` to set -the new seed value. If you don't wish to use random seed values from the cloud, -you can take control of the random seeds set by adding this code to your app: +Also see: [`Particle.syncTime()`](#particle-synctime-) ```cpp -void random_seed_from_cloud(unsigned int seed) { - // don't do anything with this. Continue with existing seed. -} +// Set the time to 2014-10-11 13:37:42 +Time.setTime(1413034662); ``` -In the example, the seed is simply ignored, so the system will continue using -whatever seed was previously set. In this case, the random seed will not be set -from the cloud, and setting the seed is left to up you. +Parameters: Unix timestamp (integer) -## EEPROM +### timeStr() -EEPROM emulation allocates a region of the device's built-in Flash memory to act as EEPROM. -Unlike "true" EEPROM, flash doesn't suffer from write "wear" with each write to -each individual address. Instead, the page suffers wear when it is filled. Each write -will add more data to the page until it is full, causing a page erase. +Return string representation for the given time. +```cpp +Serial.print(Time.timeStr()); // Wed May 21 01:08:47 2014 +``` -The EEPROM functions can be used to store small amounts of data in Flash that -will persist even after the device resets after a deep sleep or is powered off. +Returns: String -### length() -Returns the total number of bytes available in the emulated EEPROM. +_NB: In 0.3.4 and earlier, this function included a newline at the end of the returned string. This has been removed in 0.4.0._ -```c++ -// SYNTAX -size_t length = EEPROM.length(); -``` +### format() -- The Core has 127 bytes of emulated EEPROM. -- The Photon and Electron have 2047 bytes of emulated EEPROM. +Formats a time string using a configurable format. -### put() -This function will write an object to the EEPROM. You can write single values like `int` and -`float` or group multiple values together using `struct` to ensure that all values of the struct are -updated together. +```cpp +// EXAMPLE -``` -// SYNTAX -EEPROM.put(int address, object) -``` +time_t time = Time.now(); +Time.format(time, TIME_FORMAT_DEFAULT); // Sat Jan 10 08:22:04 2004 , same as Time.timeStr() -`address` is the start address (int) of the EERPOM locations to write. It must be a value between 0 -and `EEPROM.length()-1` +Time.zone(-5.25); // setup a time zone, which is part of the ISO6801 format +Time.format(time, TIME_FORMAT_ISO8601_FULL); // 2004-01-10T08:22:04-05:15 -`object` is the object data to write. The number of bytes to write is automatically determined from -the type of object. +``` -```C++ -// EXAMPLE USAGE -// Write a value (2 bytes in this case) to the EEPROM address -int addr = 10; -uint16_t value = 12345; -EEPROM.put(addr, value); +The formats available are: -// Write an object to the EEPROM address -addr = 20; -struct MyObject { - uint8_t version; - float field1; - uint16_t field2; - char name[10]; -}; -MyObject myObj = { 0, 12.34f, 25, "Test!" }; -EEPROM.put(addr, myObj); -``` +- `TIME_FORMAT_DEFAULT` +- `TIME_FORMAT_ISO8601_FULL` +- custom format based on `strftime()` -The object data is first compared to the data written in the EEPROM to avoid writing values that -haven't changed. +{{#if core}} +Note that the format function is implemented using `strftime()` which adds several kilobytes to the size of firmware. +Application firmware that has limited space available may want to consider using simpler alternatives that consume less firmware space, such as `sprintf()`. +{{/if}} -If the {{device}} loses power before the write finishes, the partially written data will be ignored. +### setFormat() -If you write several objects to EEPROM, make sure they don't overlap: the address of the second -object must be larger than the address of the first object plus the size of the first object. You -can leave empty room between objects in case you need to make the first object bigger later. +Sets the format string that is the default value used by `format()`. -### get() -This function will retrieve an object from the EEPROM. Use the same type of object you used in the -`put` call. +```cpp + +Time.setFormat(TIME_FORMAT_ISO8601_FULL); ``` -// SYNTAX -EEPROM.get(int address, object) + +In more advanced cases, you can set the format to a static string that follows +the same syntax as the `strftime()` function. + ``` +// custom formatting -`address` is the start address (int) of the EERPOM locations to read. It must be a value between 0 -and `EEPROM.length()-1` +Time.format(Time.now(), "Now it's %I:%M%p."); +// Now it's 03:21AM. -`object` is the object data that would be read. The number of bytes read is automatically determined -from the type of object. +``` + +### getFormat() + +Retrieves the currently configured format string for time formatting with `format()`. + + +### millis() + +Returns the number of milliseconds since the device began running the current program. This number will overflow (go back to zero), after approximately 49 days. + +`unsigned long time = millis();` ```C++ // EXAMPLE USAGE -// Read a value (2 bytes in this case) from EEPROM addres -int addr = 10; -uint16_t value; -EEPROM.get(addr, value); -if(value == 0xFFFF) { - // EEPROM was empty -> initialize value - value = 25; -} -// Read an object from the EEPROM addres -addr = 20; -struct MyObject { - uint8_t version; - float field1; - uint16_t field2; - char name[10]; -}; -MyObject myObj; -EEPROM.get(addr, myObj); -if(myObj.version != 0) { - // EEPROM was empty -> initialize myObj - MyObject defaultObj = { 0, 12.34f, 25, "Test!" }; - myObj = defaultObj; +unsigned long time; + +void setup() +{ + Serial.begin(9600); +} +void loop() +{ + Serial.print("Time: "); + time = millis(); + //prints time since program started + Serial.println(time); + // wait a second so as not to send massive amounts of data + delay(1000); } ``` +**Note:** +The return value for millis is an unsigned long, errors may be generated if a programmer tries to do math with other data types such as ints. -The default value of bytes in the EEPROM is 255 (hexadecimal 0xFF) so reading an object on a new -{{device}} will return an object filled with 0xFF. One trick to deal with default data is to include -a version field that you can check to see if there was valid data written in the EEPROM. +### micros() -### read() -Read a single byte of data from the emulated EEPROM. +Returns the number of microseconds since the device began running the current program. + +Firmware v0.4.3 and earlier: +- This number will overflow (go back to zero), after exactly 59,652,323 microseconds (0 .. 59,652,322) on the Core and after exactly 35,791,394 microseconds (0 .. 35,791,394) on the Photon and Electron. -``` -// SYNTAX -uint8_t value = EEPROM.read(int address); -``` -`address` is the address (int) of the EERPOM location to read + +`unsigned long time = micros();` ```C++ // EXAMPLE USAGE -// Read the value of the second byte of EEPROM -int addr = 1; -uint8_t value = EEPROM.read(addr); +unsigned long time; + +void setup() +{ + Serial.begin(9600); +} +void loop() +{ + Serial.print("Time: "); + time = micros(); + //prints time since program started + Serial.println(time); + // wait a second so as not to send massive amounts of data + delay(1000); +} ``` -When reading more than 1 byte, prefer `get()` over multiple `read()` since it's faster. +### delay() -### write() -Write a single byte of data to the emulated EEPROM. +Pauses the program for the amount of time (in milliseconds) specified as parameter. (There are 1000 milliseconds in a second.) -``` +```C++ // SYNTAX -write(int address, uint8_t value); +delay(ms); ``` -`address` is the address (int) of the EERPOM location to write to -`value` is the byte data (uint8_t) to write +`ms` is the number of milliseconds to pause *(unsigned long)* ```C++ // EXAMPLE USAGE -// Write a byte value to the second byte of EEPROM -int addr = 1; -uint8_t val = 0x45; -EEPROM.write(addr, val); -``` - -When writing more than 1 byte, prefer `put()` over multiple `write()` since it's faster and it ensures -consistent data even when power is lost while writing. +int ledPin = D1; // LED connected to digital pin D1 -### clear() -Erase all the EEPROM so that all reads will return 255 (hexadecimal 0xFF). +void setup() +{ + pinMode(ledPin, OUTPUT); // sets the digital pin as output +} -```C++ -// EXAMPLE USAGE -// Reset all EEPROM locations to 0xFF -EEPROM.clear(); +void loop() +{ + digitalWrite(ledPin, HIGH); // sets the LED on + delay(1000); // waits for a second + digitalWrite(ledPin, LOW); // sets the LED off + delay(1000); // waits for a second +} ``` +**NOTE:** +the parameter for millis is an unsigned long, errors may be generated if a programmer tries to do math with other data types such as ints. -Calling this function pauses processor execution (including code running in interrupts) for 800ms since -no instructions can be fetched from Flash while the Flash controller is busy erasing both EEPROM -pages. +### delayMicroseconds() -### hasPendingErase() -### performPendingErase() +Pauses the program for the amount of time (in microseconds) specified as parameter. There are a thousand microseconds in a millisecond, and a million microseconds in a second. -*Automatic page erase is the default behavior. This section describes optional functions the -application can call to manually control page erase for advanced use cases.* +```C++ +// SYNTAX +delayMicroseconds(us); +``` +`us` is the number of microseconds to pause *(unsigned int)* -After enough data has been written to fill the first page, the EEPROM emulation will write new data -to a second page. The first page must be erased before being written again. +```C++ +// EXAMPLE USAGE -Erasing a page of Flash pauses processor execution (including code running in interrupts) for 500ms since -no instructions can be fetched from Flash while the Flash controller is busy erasing the EEPROM -page. This could cause issues in applications that use EEPROM but rely on precise interrupt timing. +int outPin = D1; // digital pin D1 -`hasPendingErase()` lets the application developer check if a full EEPROM page needs to be erased. -When the application determines it is safe to pause processor execution to erase EEPROM it calls -`performPendingErase()`. You can call this at boot, or when your device is idle if you expect it to -run without rebooting for a long time. +void setup() +{ + pinMode(outPin, OUTPUT); // sets the digital pin as output +} -``` -// EXAMPLE USAGE -void setup() { - // Erase full EEPROM page at boot when necessary - if(EEPROM.hasPendingErase()) { - EEPROM.performPendingErase(); - } +void loop() +{ + digitalWrite(outPin, HIGH); // sets the pin on + delayMicroseconds(50); // pauses for 50 microseconds + digitalWrite(outPin, LOW); // sets the pin off + delayMicroseconds(50); // pauses for 50 microseconds } ``` -To estimate how often page erases will be necessary in your application, assume that it takes -`2*EEPROM.length()` byte writes to fill a page (it will usually be more because not all bytes will always be -updated with different values). +### isValid() -If the application never calls `performPendingErase()` then the pending page erase will be performed -when data is written using `put()` or `write()` and both pages are full. So calling -`performPendingErase()` is optional and provided to avoid the uncertainty of a potential processor -pause any time `put()` or `write()` is called. +_Since 0.6.1_ -{{#unless core}} -## Backup RAM (SRAM) +```cpp +// SYNTAX +Time.isValid(); +``` -The STM32F2xx features 4KB of backup RAM. Unlike the regular RAM memory, the backup -RAM is retained so long as power is provided to VIN or to VBAT. In particular this means that -the data in backup RAM is retained when: +Used to check if current time is valid. This function will return `true` if: +- Time has been set manually using [`Time.setTime()`](#settime-) +- Time has been successfully synchronized with the Particle Cloud. The device synchronizes time with the Particle Cloud during the handshake. The application may also manually synchronize time with Particle Cloud using [`Particle.syncTime()`](#particle-synctime-) +- Correct time has been maintained by RTC.{{#unless core}} See information on [`Backup RAM (SRAM)`](#backup-ram-sram-) for cases when RTC retains the time. RTC is part of the backup domain and retains its counters under the same conditions as Backup RAM.{{/unless}} -- the device goes into deep sleep mode -- the device is hardware or software reset (while maintaining power) -- power is removed from VIN but retained on VBAT (which will retain both the backup RAM and the RTC) +**NOTE:** When {{device}} is running in `AUTOMATIC` mode {{#unless core}}and threading is disabled {{/unless}} this function will block if current time is not valid and there is an active connection to Particle Cloud. Once {{device}} synchronizes the time with Particle Cloud or the connection to Particle Cloud is lost, `Time.isValid()` will return its current state. This function is also implicitly called by any `Time` function that returns current time or date (e.g. `Time.hour()`/`Time.now()`/etc). -Note that _if neither VIN or VBAT is powered then the contents of the backup RAM will be lost; for data to be -retained, the device needs a power source._ For persistent storage of data through a total power loss, please use the [EEPROM](#eeprom) library. +```cpp +// Print true or false depending on whether current time is valid +Serial.print(Time.isValid()); +``` -Power Conditions and how they relate to Backup RAM initilization and data retention: +```cpp +void setup() +{ + // Wait for time to be synchronized with Particle Cloud (requires active connection) + waitFor(Time.isValid, 60000); +} -| Power Down Method | Power Up Method | When VIN Powered | When VBAT Powered | SRAM Initialized | SRAM Retained | -| -: | :- | :-: | :-: | :-: | :-: | -| Power removed on VIN and VBAT | Power applied on VIN | - | No[1] | Yes | No | -| Power removed on VIN and VBAT | Power applied on VIN | - | Yes | Yes | No | -| Power removed on VIN | Power applied on VIN | - | Yes | No | Yes | -| System.sleep(SLEEP_MODE_DEEP) | Rising edge on WKP pin, or Hard Reset | Yes | Yes/No | No | Yes | -| System.sleep(SLEEP_MODE_DEEP,10) | RTC alarm after 10 seconds | Yes | Yes/No | No | Yes | -| System.reset() | Boot after software reset | Yes | Yes/No | No | Yes | -| Hard reset | Boot after hard reset | Yes | Yes/No | No | Yes | +void loop() +{ + // Print current time + Serial.println(Time.timeStr()); +} -[1] Note: If VBAT is floating when powering up for the first time, SRAM remains uninitialized. When using this feature for Backup RAM, it is recommended to have VBAT connected to a 3V3 or a known good power source on system first boot. When using this feature for Extra RAM, it is recommended to jumper VBAT to GND to ensure it always initializes on system first boot. +``` -### Storing data in Backup RAM (SRAM) +{{#if has-interrupts}} + +## Interrupts + +Interrupts are a way to write code that is run when an external event occurs. +As a general rule, interrupt code should be very fast, and non-blocking. This means +performing transfers, such as I2C, Serial, TCP should not be done as part of the +interrupt handler. Rather, the interrupt handler can set a variable which instructs +the main loop that the event has occurred. + +### attachInterrupt() + +Specifies a function to call when an external interrupt occurs. Replaces any previous function that was attached to the interrupt. + +**NOTE:** +`pinMode()` MUST be called prior to calling attachInterrupt() to set the desired mode for the interrupt pin (INPUT, INPUT_PULLUP or INPUT_PULLDOWN). + +External interrupts are supported on the following pins: + +- Core: D0, D1, D2, D3, D4, A0, A1, A3, A4, A5, A6, A7 +- Photon: All pins with the exception of D0 and A5 (since at present Mode Button external interrupt(EXTI) line is shared with D0, A5). Also please note following are the pins for which EXTI lines are shared so only one can work at a time: + - D1, A4 + - D2, A0, A3 + - D3, DAC + - D4, A1 + +``` +// SYNTAX +attachInterrupt(pin, function, mode); +attachInterrupt(pin, function, mode, priority); +attachInterrupt(pin, function, mode, priority, subpriority); +``` + +*Parameters:* + +- `pin`: the pin number +- `function`: the function to call when the interrupt occurs; this function must take no parameters and return nothing. This function is sometimes referred to as an *interrupt service routine* (ISR). +- `mode`: defines when the interrupt should be triggered. Three constants are predefined as valid values: + - CHANGE to trigger the interrupt whenever the pin changes value, + - RISING to trigger when the pin goes from low to high, + - FALLING for when the pin goes from high to low. +- `priority` (optional): the priority of this interrupt. Default priority is 13. Lower values increase the priority of the interrupt. +- `subpriority` (optional): the subpriority of this interrupt. Default subpriority is 0. + +The function returns a boolean whether the ISR was successfully attached (true) or not (false). + +```C++ +// EXAMPLE USAGE + +void blink(void); +int ledPin = D1; +volatile int state = LOW; + +void setup() +{ + pinMode(ledPin, OUTPUT); + pinMode(D2, INPUT_PULLUP); + attachInterrupt(D2, blink, CHANGE); +} + +void loop() +{ + digitalWrite(ledPin, state); +} + +void blink() +{ + state = !state; +} +``` + +You can attach a method in a C++ object as an interrupt handler. + +```cpp +class Robot { + public: + Robot() { + pinMode(D2, INPUT_PULLUP); + attachInterrupt(D2, &Robot::handler, this, CHANGE); + } + void handler() { + // do something on interrupt + } +}; + +Robot myRobot; +// nothing else needed in setup() or loop() +``` + +**Using Interrupts:** +Interrupts are useful for making things happen automatically in microcontroller programs, and can help solve timing problems. Good tasks for using an interrupt may include reading a rotary encoder, or monitoring user input. + +If you wanted to insure that a program always caught the pulses from a rotary encoder, so that it never misses a pulse, it would make it very tricky to write a program to do anything else, because the program would need to constantly poll the sensor lines for the encoder, in order to catch pulses when they occurred. Other sensors have a similar interface dynamic too, such as trying to read a sound sensor that is trying to catch a click, or an infrared slot sensor (photo-interrupter) trying to catch a coin drop. In all of these situations, using an interrupt can free the microcontroller to get some other work done while not missing the input. + +**About Interrupt Service Routines:** +ISRs are special kinds of functions that have some unique limitations most other functions do not have. An ISR cannot have any parameters, and they shouldn't return anything. + +Generally, an ISR should be as short and fast as possible. If your sketch uses multiple ISRs, only one can run at a time, other interrupts will be executed after the current one finishes in an order that depends on the priority they have. `millis()` relies on interrupts to count, so it will never increment inside an ISR. Since `delay()` requires interrupts to work, it will not work if called inside an ISR. Using `delayMicroseconds()` will work as normal. + +Typically global variables are used to pass data between an ISR and the main program. To make sure variables shared between an ISR and the main program are updated correctly, declare them as `volatile`. + + +### detachInterrupt() + +Turns off the given interrupt. + +``` +// SYNTAX +detachInterrupt(pin); +``` + +`pin` is the pin number of the interrupt to disable. + + +### interrupts() + +Re-enables interrupts (after they've been disabled by `noInterrupts()`). Interrupts allow certain important tasks to happen in the background and are enabled by default. Some functions will not work while interrupts are disabled, and incoming communication may be ignored. Interrupts can slightly disrupt the timing of code, however, and may be disabled for particularly critical sections of code. + +```C++ +// EXAMPLE USAGE + +void setup() {} + +void loop() +{ + noInterrupts(); // disable interrupts + // + // put critical, time-sensitive code here + // + interrupts(); // enable interrupts + // + // other code here + // +} +``` + +`interrupts()` neither accepts a parameter nor returns anything. + +### noInterrupts() + +Disables interrupts (you can re-enable them with `interrupts()`). Interrupts allow certain important tasks to happen in the background and are enabled by default. Some functions will not work while interrupts are disabled, and incoming communication may be ignored. Interrupts can slightly disrupt the timing of code, however, and may be disabled for particularly critical sections of code. + +```C++ +// SYNTAX +noInterrupts(); +``` + +`noInterrupts()` neither accepts a parameter nor returns anything. + +{{/if}} {{!-- has-interrupts --}} + +{{#if has-software-timers}} + +## Software Timers + +_Since 0.4.7 This feature is available on the Photon, P1 and Electron out the box. On the Core, the +`freertos4core` Particle library (Timers.ino example found here) should be used to add FreeRTOS to the core._ + +Software Timers provide a way to have timed actions in your program. FreeRTOS provides the ability to have up to 10 Software Timers at a time with a minimum resolution of 1 millisecond. It is common to use millis() based "timers" though exact timing is not always possible (due to other program delays). Software timers are maintained by FreeRTOS and provide a more reliable method for running timed actions using callback functions. Please note that Software Timers are "chained" and will be serviced sequentially when several timers trigger simultaneously, thus requiring special consideration when writing callback functions. + +```cpp +// EXAMPLE + +void print_every_second() +{ + static int count = 0; + Serial.println(count++); +} + +Timer timer(1000, print_every_second); + +void setup() +{ + Serial.begin(9600); + timer.start(); +} +``` + +Timers may be started, stopped, reset within a user program or an ISR. They may also be "disposed", removing them from the (max. 10) active timer list. + +The timer callback is similar to an interrupt - it shouldn't block. However, it is less restrictive than an interrupt. If the code does block, the system will not crash - the only consequence is that other software timers that should have triggered will be delayed until the blocking timer callback function returns. + +// SYNTAX + +`Timer timer(period, callback, one_shot)` + +- `period` is the period of the timer in milliseconds (unsigned int) +- `callback` is the callback function which gets called when the timer expires. +- `one_shot` (optional, since 0.4.9) when `true`, the timer is fired once and then stopped automatically. The default is `false` - a repeating timer. + + +{{/if}} {{!-- has-software-timers --}} + +### Class member callbacks + +_Since 0.4.9_ + +A class member function can be used as a callback using this syntax to create the timer: + +`Timer timer(period, callback, instance, one_shot)` + +- `period` is the period of the timer in milliseconds (unsigned int) +- `callback` is the class member function which gets called when the timer expires. +- `instance` the instance of the class to call the callback function on. +- `one_shot` (optional, since 0.4.9) when `true`, the timer is fired once and then stopped automatically. The default is `false` - a repeating timer. + + +``` +// Class member function callback example + +class CallbackClass +{ +public: + void onTimeout(); +} + +CallbackClass callback; +Timer t(1000, &CallbackClass::onTimeout, callback); + +``` + + +### start() + +Starts a stopped timer (a newly created timer is stopped). If `start()` is called for a running timer, it will be reset. + +`start()` + +```C++ +// EXAMPLE USAGE +timer.start(); // starts timer if stopped or resets it if started. + +``` + +### stop() + +Stops a running timer. + +`stop()` + +```C++ +// EXAMPLE USAGE +timer.stop(); // stops a running timer. + +``` + +### changePeriod() + +Changes the period of a previously created timer. It can be called to change the period of an running or stopped timer. + +`changePeriod(newPeriod)` + +`newPeriod` is the new timer period (unsigned int) + +```C++ +// EXAMPLE USAGE +timer.changePeriod(1000); // Reset period of timer to 1000ms. + +``` + + +### reset() + +Resets a timer. If a timer is running, it will reset to "zero". If a timer is stopped, it will be started. + +`reset()` + +```C++ +// EXAMPLE USAGE +timer.reset(); // reset timer if running, or start timer if stopped. + +``` + +### startFromISR() +### stopFromISR() +### resetFromISR() +### changePeriodFromISR() + +`startFromISR()` +`stopFromISR()` +`resetFromISR()` +`changePeriodFromISR()` + +Start, stop and reset a timer or change a timer's period (as above) BUT from within an ISR. These functions MUST be called when doing timer operations within an ISR. + +```C++ +// EXAMPLE USAGE +timer.startFromISR(); // WITHIN an ISR, starts timer if stopped or resets it if started. + +timer.stopFromISR(); // WITHIN an ISR,stops a running timer. + +timer.resetFromISR(); // WITHIN an ISR, reset timer if running, or start timer if stopped. + +timer.changePeriodFromISR(newPeriod); // WITHIN an ISR, change the timer period. +``` + +### dispose() + +`dispose()` + +Stop and remove a timer from the (max. 10) timer list, freeing a timer "slot" in the list. + +```C++ +// EXAMPLE USAGE +timer.dispose(); // stop and delete timer from timer list. + +``` + +### isActive() + +_Since 0.5.0_ + +`bool isActive()` + +Returns `true` if the timer is in active state (pending), or `false` otherwise. + +```C++ +// EXAMPLE USAGE +if (timer.isActive()) { + // ... +} +``` + +{{#if has-application-watchdog}} + +## Application Watchdog + +_Since 0.5.0_ + +The Application Watchdog is a software-implemented watchdog using a critical-priority thread that wakes up at a given timeout interval to see if the application has checked in. + +If the application has not exited loop, or called Particle.process() within the given timeout, or called `ApplicationWatchdog.checkin()`, the watchdog calls the given timeout function, which is typically `System.reset`. This could also be a user defined function that takes care of critical tasks before finally calling `System.reset`. + + +```cpp +// SYNTAX +// declare a global watchdog instance +ApplicationWatchdog wd(timeout_milli_seconds, timeout_function_to_call, stack_size); + +// default stack_size of 512 bytes is used +ApplicationWatchdog wd(timeout_milli_seconds, timeout_function_to_call); + +// EXAMPLE USAGE +// reset the system after 60 seconds if the application is unresponsive +ApplicationWatchdog wd(60000, System.reset); + +void loop() { + while (some_long_process_within_loop) { + wd.checkin(); // resets the AWDT count + } +} +// AWDT count reset automatically after loop() ends +``` + +A default `stack_size` of 512 is used for the thread. `stack_size` is an optional parameter. The stack can be made larger or smaller as needed. This is the amount of memory needed to support the thread and function that is called. In practice, on the Photon (v0.5.0) calling the `System.reset` function requires approx. 170 bytes of memory. If not enough memory is allocated, the application will crash due to a Stack Overflow. The RGB LED will flash a [red SOS pattern, followed by 13 blinks](/guide/getting-started/modes/photon/#red-flash-sos). + +The application watchdog requires interrupts to be active in order to function. Enabling the hardware watchdog in combination with this is recommended, so that the system resets in the event that interrupts are not firing. + +{{/if}} {{!-- has-application-watchdog --}} + +## Math + +Note that in addition to functions outlined below all of the newlib math functions described at [sourceware.org](https://sourceware.org/newlib/libm.html) are also available for use by simply including the math.h header file thus: + +`#include "math.h"` + +### min() + +Calculates the minimum of two numbers. + +`min(x, y)` + +`x` is the first number, any data type +`y` is the second number, any data type + +The functions returns the smaller of the two numbers. + +```C++ +// EXAMPLE USAGE +sensVal = min(sensVal, 100); // assigns sensVal to the smaller of sensVal or 100 + // ensuring that it never gets above 100. +``` + +**NOTE:** +Perhaps counter-intuitively, max() is often used to constrain the lower end of a variable's range, while min() is used to constrain the upper end of the range. + +**WARNING:** +Because of the way the min() function is implemented, avoid using other functions inside the brackets, it may lead to incorrect results + +```C++ +min(a++, 100); // avoid this - yields incorrect results + +a++; +min(a, 100); // use this instead - keep other math outside the function +``` + +### max() + +Calculates the maximum of two numbers. + +`max(x, y)` + +`x` is the first number, any data type +`y` is the second number, any data type + +The functions returns the larger of the two numbers. + +```C++ +// EXAMPLE USAGE +sensVal = max(senVal, 20); // assigns sensVal to the larger of sensVal or 20 + // (effectively ensuring that it is at least 20) +``` + +**NOTE:** +Perhaps counter-intuitively, max() is often used to constrain the lower end of a variable's range, while min() is used to constrain the upper end of the range. + +**WARNING:** +Because of the way the max() function is implemented, avoid using other functions inside the brackets, it may lead to incorrect results + +```C++ +max(a--, 0); // avoid this - yields incorrect results + +a--; // use this instead - +max(a, 0); // keep other math outside the function +``` + +### abs() + +Computes the absolute value of a number. + +`abs(x);` + +where `x` is the number + +The function returns `x` if `x` is greater than or equal to `0` +and returns `-x` if `x` is less than `0`. + +**WARNING:** +Because of the way the abs() function is implemented, avoid using other functions inside the brackets, it may lead to incorrect results. + +```C++ +abs(a++); // avoid this - yields incorrect results + +a++; // use this instead - +abs(a); // keep other math outside the function +``` + +### constrain() + +Constrains a number to be within a range. + +`constrain(x, a, b);` + +`x` is the number to constrain, all data types +`a` is the lower end of the range, all data types +`b` is the upper end of the range, all data types + +The function will return: +`x`: if x is between `a` and `b` +`a`: if `x` is less than `a` +`b`: if `x` is greater than `b` + +```C++ +// EXAMPLE USAGE +sensVal = constrain(sensVal, 10, 150); +// limits range of sensor values to between 10 and 150 +``` + +### map() + +```C++ +// EXAMPLE USAGE + +// Map an analog value to 8 bits (0 to 255) +void setup() { + pinMode(D1, OUTPUT); +} + +void loop() +{ + int val = analogRead(A0); + val = map(val, 0, 4095, 0, 255); + analogWrite(D1, val); +} +``` + +Re-maps a number from one range to another. That is, a value of fromLow would get mapped to `toLow`, a `value` of `fromHigh` to `toHigh`, values in-between to values in-between, etc. + +`map(value, fromLow, fromHigh, toLow, toHigh);` + +Does not constrain values to within the range, because out-of-range values are sometimes intended and useful. The `constrain()` function may be used either before or after this function, if limits to the ranges are desired. + +Note that the "lower bounds" of either range may be larger or smaller than the "upper bounds" so the `map()` function may be used to reverse a range of numbers, for example + +`y = map(x, 1, 50, 50, 1);` + +The function also handles negative numbers well, so that this example + +`y = map(x, 1, 50, 50, -100);` + +is also valid and works well. + +The `map()` function uses integer math so will not generate fractions, when the math might indicate that it should do so. Fractional remainders are truncated, and are not rounded or averaged. + +*Parameters:* + +- `value`: the number to map +- `fromLow`: the lower bound of the value's current range +- `fromHigh`: the upper bound of the value's current range +- `toLow`: the lower bound of the value's target range +- `toHigh`: the upper bound of the value's target range + +The function returns the mapped value + +*Appendix:* +For the mathematically inclined, here's the whole function + +```C++ +long map(long x, long in_min, long in_max, long out_min, long out_max) +{ + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} +``` + +### pow() + +Calculates the value of a number raised to a power. `pow()` can be used to raise a number to a fractional power. This is useful for generating exponential mapping of values or curves. + +`pow(base, exponent);` + +`base` is the number *(float)* +`exponent` is the power to which the base is raised *(float)* + +The function returns the result of the exponentiation *(double)* + +EXAMPLE **TBD** + +### sqrt() + +Calculates the square root of a number. + +`sqrt(x)` + +`x` is the number, any data type + +The function returns the number's square root *(double)* + +## Random Numbers + +The firmware incorporates a pseudo-random number generator. + +### random() + +Retrieves the next random value, restricted to a given range. + + `random(max);` + +Parameters + +- `max` - the upper limit of the random number to retrieve. + +Returns: a random value between 0 and up to, but not including `max`. + +```c++ +int r = random(10); +// r is >= 0 and < 10 +// The smallest value returned is 0 +// The largest value returned is 9 +``` + + NB: When `max` is 0, the result is always 0. + +--- + +`random(min,max);` + +Parameters: + + - `min` - the lower limit (inclusive) of the random number to retrieve. + - `max` - the upper limit (exclusive) of the random number to retrieve. + +Returns: a random value from `min` and up to, but not including `max`. + + +```c++ +int r = random(10, 100); +// r is >= 10 and < 100 +// The smallest value returned is 10 +// The largest value returned is 99 +``` + + NB: If `min` is greater or equal to `max`, the result is always 0. + +### randomSeed() + +`randomSeed(newSeed);` + +Parameters: + + - `newSeed` - the new random seed + +The pseudorandom numbers produced by the firmware are derived from a single value - the random seed. +The value of this seed fully determines the sequence of random numbers produced by successive +calls to `random()`. Using the same seed on two separate runs will produce +the same sequence of random numbers, and in contrast, using different seeds +will produce a different sequence of random numbers. + +On startup, the default random seed is [set by the system](http://www.cplusplus.com/reference/cstdlib/srand/) to 1. +Unless the seed is modified, the same sequence of random numbers would be produced each time +the system starts. + +Fortunately, when the device connects to the cloud, it receives a very randomized seed value, +which is used as the random seed. So you can be sure the random numbers produced +will be different each time your program is run. + + +*** Disable random seed from the cloud *** + +When the device receives a new random seed from the cloud, it's passed to this function: + +``` +void random_seed_from_cloud(unsigned int seed); +``` + +The system implementation of this function calls `randomSeed()` to set +the new seed value. If you don't wish to use random seed values from the cloud, +you can take control of the random seeds set by adding this code to your app: + +```cpp +void random_seed_from_cloud(unsigned int seed) { + // don't do anything with this. Continue with existing seed. +} +``` + +In the example, the seed is simply ignored, so the system will continue using +whatever seed was previously set. In this case, the random seed will not be set +from the cloud, and setting the seed is left to up you. + +{{#if has-eeprom}} + +## EEPROM + +EEPROM emulation allocates a region of the device's built-in Flash memory to act as EEPROM. +Unlike "true" EEPROM, flash doesn't suffer from write "wear" with each write to +each individual address. Instead, the page suffers wear when it is filled. Each write +will add more data to the page until it is full, causing a page erase. + +The EEPROM functions can be used to store small amounts of data in Flash that +will persist even after the device resets after a deep sleep or is powered off. + +### length() +Returns the total number of bytes available in the emulated EEPROM. + +```c++ +// SYNTAX +size_t length = EEPROM.length(); +``` + +- The Core has 127 bytes of emulated EEPROM. +- The Photon and Electron have 2047 bytes of emulated EEPROM. + +### put() +This function will write an object to the EEPROM. You can write single values like `int` and +`float` or group multiple values together using `struct` to ensure that all values of the struct are +updated together. + +``` +// SYNTAX +EEPROM.put(int address, object) +``` + +`address` is the start address (int) of the EERPOM locations to write. It must be a value between 0 +and `EEPROM.length()-1` + +`object` is the object data to write. The number of bytes to write is automatically determined from +the type of object. + +```C++ +// EXAMPLE USAGE +// Write a value (2 bytes in this case) to the EEPROM address +int addr = 10; +uint16_t value = 12345; +EEPROM.put(addr, value); + +// Write an object to the EEPROM address +addr = 20; +struct MyObject { + uint8_t version; + float field1; + uint16_t field2; + char name[10]; +}; +MyObject myObj = { 0, 12.34f, 25, "Test!" }; +EEPROM.put(addr, myObj); +``` + +The object data is first compared to the data written in the EEPROM to avoid writing values that +haven't changed. + +If the {{device}} loses power before the write finishes, the partially written data will be ignored. + +If you write several objects to EEPROM, make sure they don't overlap: the address of the second +object must be larger than the address of the first object plus the size of the first object. You +can leave empty room between objects in case you need to make the first object bigger later. + +### get() +This function will retrieve an object from the EEPROM. Use the same type of object you used in the +`put` call. + +``` +// SYNTAX +EEPROM.get(int address, object) +``` + +`address` is the start address (int) of the EERPOM locations to read. It must be a value between 0 +and `EEPROM.length()-1` + +`object` is the object data that would be read. The number of bytes read is automatically determined +from the type of object. + +```C++ +// EXAMPLE USAGE +// Read a value (2 bytes in this case) from EEPROM addres +int addr = 10; +uint16_t value; +EEPROM.get(addr, value); +if(value == 0xFFFF) { + // EEPROM was empty -> initialize value + value = 25; +} + +// Read an object from the EEPROM addres +addr = 20; +struct MyObject { + uint8_t version; + float field1; + uint16_t field2; + char name[10]; +}; +MyObject myObj; +EEPROM.get(addr, myObj); +if(myObj.version != 0) { + // EEPROM was empty -> initialize myObj + MyObject defaultObj = { 0, 12.34f, 25, "Test!" }; + myObj = defaultObj; +} +``` + +The default value of bytes in the EEPROM is 255 (hexadecimal 0xFF) so reading an object on a new +{{device}} will return an object filled with 0xFF. One trick to deal with default data is to include +a version field that you can check to see if there was valid data written in the EEPROM. + +### read() +Read a single byte of data from the emulated EEPROM. + +``` +// SYNTAX +uint8_t value = EEPROM.read(int address); +``` + +`address` is the address (int) of the EERPOM location to read + +```C++ +// EXAMPLE USAGE + +// Read the value of the second byte of EEPROM +int addr = 1; +uint8_t value = EEPROM.read(addr); +``` + +When reading more than 1 byte, prefer `get()` over multiple `read()` since it's faster. + +### write() +Write a single byte of data to the emulated EEPROM. + +``` +// SYNTAX +write(int address, uint8_t value); +``` + +`address` is the address (int) of the EERPOM location to write to +`value` is the byte data (uint8_t) to write + +```C++ +// EXAMPLE USAGE + +// Write a byte value to the second byte of EEPROM +int addr = 1; +uint8_t val = 0x45; +EEPROM.write(addr, val); +``` + +When writing more than 1 byte, prefer `put()` over multiple `write()` since it's faster and it ensures +consistent data even when power is lost while writing. + +### clear() +Erase all the EEPROM so that all reads will return 255 (hexadecimal 0xFF). + +```C++ +// EXAMPLE USAGE +// Reset all EEPROM locations to 0xFF +EEPROM.clear(); +``` + +Calling this function pauses processor execution (including code running in interrupts) for 800ms since +no instructions can be fetched from Flash while the Flash controller is busy erasing both EEPROM +pages. + +### hasPendingErase() +### performPendingErase() + +*Automatic page erase is the default behavior. This section describes optional functions the +application can call to manually control page erase for advanced use cases.* + +After enough data has been written to fill the first page, the EEPROM emulation will write new data +to a second page. The first page must be erased before being written again. + +Erasing a page of Flash pauses processor execution (including code running in interrupts) for 500ms since +no instructions can be fetched from Flash while the Flash controller is busy erasing the EEPROM +page. This could cause issues in applications that use EEPROM but rely on precise interrupt timing. + +`hasPendingErase()` lets the application developer check if a full EEPROM page needs to be erased. +When the application determines it is safe to pause processor execution to erase EEPROM it calls +`performPendingErase()`. You can call this at boot, or when your device is idle if you expect it to +run without rebooting for a long time. + +``` +// EXAMPLE USAGE +void setup() { + // Erase full EEPROM page at boot when necessary + if(EEPROM.hasPendingErase()) { + EEPROM.performPendingErase(); + } +} +``` + +To estimate how often page erases will be necessary in your application, assume that it takes +`2*EEPROM.length()` byte writes to fill a page (it will usually be more because not all bytes will always be +updated with different values). + +If the application never calls `performPendingErase()` then the pending page erase will be performed +when data is written using `put()` or `write()` and both pages are full. So calling +`performPendingErase()` is optional and provided to avoid the uncertainty of a potential processor +pause any time `put()` or `write()` is called. + +{{/if}} + +{{#if has-backup-ram}} +## Backup RAM (SRAM) + +The STM32F2xx features 4KB of backup RAM (3068 bytes for system firmware v0.6.0-rc1 and later) of which is available to the user. Unlike the regular RAM memory, the backup RAM is retained so long as power is provided to VIN or to VBAT. In particular this means that the data in backup RAM is retained when: + +- the device goes into deep sleep mode +- the device is hardware or software reset (while maintaining power) +- power is removed from VIN but retained on VBAT (which will retain both the backup RAM and the RTC) + +Note that _if neither VIN or VBAT is powered then the contents of the backup RAM will be lost; for data to be +retained, the device needs a power source._ For persistent storage of data through a total power loss, please use the [EEPROM](#eeprom) library. + +Power Conditions and how they relate to Backup RAM initilization and data retention: + +| Power Down Method | Power Up Method | When VIN Powered | When VBAT Powered | SRAM Initialized | SRAM Retained | +| -: | :- | :-: | :-: | :-: | :-: | +| Power removed on VIN and VBAT | Power applied on VIN | - | No[1] | Yes | No | +| Power removed on VIN and VBAT | Power applied on VIN | - | Yes | Yes | No | +| Power removed on VIN | Power applied on VIN | - | Yes | No | Yes | +| System.sleep(SLEEP_MODE_DEEP) | Rising edge on WKP pin, or Hard Reset | Yes | Yes/No | No | Yes | +| System.sleep(SLEEP_MODE_DEEP,10) | RTC alarm after 10 seconds | Yes | Yes/No | No | Yes | +| System.reset() | Boot after software reset | Yes | Yes/No | No | Yes | +| Hard reset | Boot after hard reset | Yes | Yes/No | No | Yes | + +[1] Note: If VBAT is floating when powering up for the first time, SRAM remains uninitialized. When using this feature for Backup RAM, it is recommended to have VBAT connected to a 3V3 or a known good power source on system first boot. When using this feature for Extra RAM, it is recommended to jumper VBAT to GND to ensure it always initializes on system first boot. + +### Storing data in Backup RAM (SRAM) With regular RAM, data is stored in RAM by declaring variables. -```C++ -// regular variables stored in RAM -float lastTemperature; -int numberOfPresses; -int numberOfTriesRemaining = 10; -``` +```C++ +// regular variables stored in RAM +float lastTemperature; +int numberOfPresses; +int numberOfTriesRemaining = 10; +``` + +This tells the system to store these values in RAM so they can be changed. The +system takes care of giving them initial values. Before they are set, +they will have the initial value 0 if an initial value isn't specified. + +Variables stored in backup RAM follow a similar scheme but use an additional keyword `retained`: + +```C++ +// retained variables stored in backup RAM +retained float lastTemperature; +retained int numberOfPresses; +retained int numberOfTriesRemaining = 10; +``` + +A `retained` variable is similar to a regular variable, with some key differences: + +- it is stored in backup RAM - no space is used in regular RAM +- instead of being initialized on each program start, `retained` variables are initialized +when the device is first powered on (with VIN, from being powered off with VIN and VBAT completely removed). +When the device is powered on, the system takes care of setting these variables to their initial values. +`lastTemperature` and `numberOfPresses` would be initialized to 0, while `numberOfTriesRemaining` would be initialized to 10. +- the last value set on the variable is retained *as long as the device is powered from VIN or VBAT and is not hard reset*. + +`retained` variables can be updated freely just as with regular RAM variables and operate +just as fast as regular RAM variables. + +Here's some typical use cases for `retained` variables: + +- storing data for use after waking up from deep sleep +- storing data for use after power is removed on VIN, while power is still applied to VBAT (with coin cell battery or super capacitor) +- storing data for use after a hardware or software reset + +Finally, if you don't need the persistence of `retained` variables, you +can consider them simply as extra RAM to use. + +```C++ +// EXAMPLE USAGE +STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY)); + +retained int value = 10; + +void setup() { + Serial.begin(9600); +} + +void loop() { + Serial.println(value); + value = 20; + Serial.println(value); + delay(100); // Give the serial TX buffer a chance to empty + System.sleep(SLEEP_MODE_DEEP, 10); + // Or try a software reset + // System.reset(); +} + +/* OUTPUT + * + * 10 + * 20 + * DEEP SLEEP for 10 seconds + * 20 (value is retained as 20) + * 20 + * + */ +``` + +### Enabling Backup RAM (SRAM) + +Backup RAM is disabled by default, since it does require some maintenance power +which may not be desired on some low-powered projects. Backup RAM consumes roughly +5uA or less on VIN and 9uA or less on VBAT. + +Backup RAM is enabled with this code (to be placed at the top of your application outside of any functions): + +```cpp + +STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY)); + +``` + +### Making changes to the layout or types of retained variables + +When adding new `retained` variables to an existing set of `retained` variables, +it's a good idea to add them after the existing variables. this ensures the +existing retained data is still valid even with the new code. + +For example, if we wanted to add a new variable `char name[50]` we should add this after +the existing `retained` variables: + +``` +retained float lastTemperature; +retained int numberOfPresses; +retained int numberOfTriesRemaining = 10; +retained char name[50]; +``` + +If instead we added `name` to the beginning or middle of the block of variables, +the program would end up reading the stored values of the wrong variables. This is +because the new code would be expecting to find the variables in a different memory location. + +Similarly, you should avoid changing the type of your variables as this will also +alter the memory size and location of data in memory. + +This caveat is particularly important when updating firmware without power-cycling +the device, which uses a software reset to reboot the device. This will allow previously +`retained` variables to persist. + +During development, a good suggestion to avoid confusion is to design your application to work +correctly when power is being applied for the first time, and all `retained` variables are +initialized. If you must rearrange variables, simply power down the device (VIN and VBAT) +after changes are made to allow reinitialization of `retained` variables on the next power +up of the device. + +It's perfectly fine to mix regular and `retained` variables, but for clarity we recommend +keeping the `retained` variables in their own separate block. In this way it's easier to recognize +when new `retained` variables are added to the end of the list, or when they are rearranged. + + +{{/if}} {{!-- has-backup-ram --}} + +## Macros + +### STARTUP() + +_Since 0.4.5_ + +Typically an application will have its initialization code in the `setup()` function. +This works well if a delay of a few seconds from power on/reset is acceptable. + +In other cases, the application wants to have code run as early as possible, before the cloud or network connection +are initialized. The `STARTUP()` function instructs the system to execute the code early on in startup. + +```cpp +void setup_the_fundulating_conbobulator() +{ + pinMode(D3, OUTPUT); + digitalWrite(D3, HIGH); +} + +// The STARTUP call is placed outside of any other function +// What goes inside is any valid code that can be executed. Here, we use a function call. +// Using a single function is preferable to having several `STARTUP()` calls. +STARTUP( setup_the_fundulating_conbobulator() ); + +``` + +The code referenced by `STARTUP()` is executed very early in the startup sequence, so it's best suited +to initializing digital I/O and peripherals. Networking setup code should still be placed in `setup()`. +{{#if has-wifi-antenna-switch}} +Although there is one notable exception - `WiFi.selectAntenna()` should be called from `STARTUP()` to select the default antenna before the Wi-Fi connection is made. +{{/if}} + +_Note that when startup code performs digital I/O, there will still be a period of at least few hundred milliseconds +where the I/O pins are in their default power-on state, namely `INPUT`. Circuits should be designed with this +in mind, using pullup/pulldown resistors as appropriate._ + +### PRODUCT_ID() + +When preparing software for your product, it is essential to include your product ID and version at the top of the firmware source code. + +```cpp +// EXAMPLE +PRODUCT_ID(94); // replace by your product ID +PRODUCT_VERSION(1); // increment each time you upload to the console +``` + +You can find more details about the product ID and how to get yours in the [_Console_ guide.](/guide/tools-and-features/console/#your-product-id) + +## System Events + +_Since 0.4.9_ + +### System Events Overview + +System events are messages sent by the system and received by application code. They inform the application about changes in the system, such as when the system has entered setup mode, or when an Over-the-Air (OTA) update starts, or when the system is about to reset. + +System events are recieved by the application by registering a handler. The handler has this general format: + +``` +void handler(system_event_t event, int data, void* moredata); +``` + +Unused parameters can be removed from right to left, giving these additional function signatures: + +``` +void handler(system_event_t event, int data); +void handler(system_event_t event); +void handler(); +``` + +Here's an example of an application that listens for `reset` events so that the application is notified the device is about to reset. The application publishes a reset message to the cloud and turns off connected equipment before returning from the handler, allowing the device to reset. + +``` +void reset_handler() +{ + // turn off the crankenspitzen + digitalWrite(D6, LOW); + // tell the world what we are doing + Particle.publish("reset", "going down for reboot NOW!"); +} + +void setup() +{ + // register the reset handler + System.on(reset, reset_handler); +} +``` + +Some event types provide additional information. For example the `button_click` event provides a parameter with the number of button clicks: + +``` +void button_clicked(system_event_t event, int param) +{ + int times = system_button_clicks(param); + Serial.printlnf("button was clicked %d times", times); +} +``` + +#### Registering multiple events with the same handler + +It's possible to subscribe to multiple events with the same handler in cases where you want the same handler to be notified for all the events. For example: + +``` +void handle_all_the_events(system_event_t event, int param) +{ + Serial.printlnf("got event %d with value %d", event, param); +} + +void setup() +{ + // listen for Wi-Fi Listen events and Firmware Update events + System.on(wifi_listen+firmware_update, handle_all_the_events); +} +``` + +To subscribe to all events, there is the placeholder `all_events`: + +``` +void setup() +{ + // listen for network events and firmware update events + System.on(all_events, handle_all_the_events); +} +``` + +### System Events Reference + +These are the system events produced by the system, their numeric value (what you will see when printing the system event to Serial) and details of how to handle the parameter value. The version of firmware these events became available is noted in the first column below. + +| Since | Event Name | ID | Description | Parameter | +|-------|------------|----|-------------|-----------| +| | setup_begin | 2 | signals the device has entered setup mode | not used | +| | setup_update | 4 | periodic event signalling the device is still in setup mode. | milliseconds since setup mode was started | +| | setup_end | 8 | signals setup mode was exited | time in ms since setup mode was started | +| | network_credentials | 16 | network credentials were changed | `network_credentials_added` or `network_credentials_cleared` | +| 0.6.1 | network_status | 32 | network connection status | one of `network_status_powering_on`, `network_status_on`, `network_status_powering_off`, `network_status_off`, `network_status_connecting`, `network_status_connected` | +| 0.6.1 | cloud_status | 64 | cloud connection status | one of `cloud_status_connecting`, `cloud_status_connected`, `cloud_status_disconnecting`, `cloud_status_disconnected` | +| | button_status | 128 | button pressed or released | the duration in ms the button was pressed: 0 when pressed, >0 on release. | +| | firmware_update | 256 | firmware update status | one of `firmware_update_begin`, `firmware_update_progress`, `firmware_update_complete`, `firmware_update_failed` | +| | firmware_update_pending | 512 | notifies the application that a firmware update is available. This event is sent even when updates are disabled, giving the application chance to re-enable firmware updates with `System.enableUpdates()` | not used | +| | reset_pending | 1024 | notifies the application that the system would like to reset. This event is sent even when resets are disabled, giving the application chance to re-enable resets with `System.enableReset()` | not used | +| | reset | 2048 | notifies that the system will reset once the application has completed handling this event | not used | +| | button_click | 4096 | event sent each time setup button is clicked. | `int clicks = system_button_clicks(param); ` retrieves the number of clicks so far. | +| | button_final_click | 8192 | sent after a run of one or more clicks not followed by additional clicks. Unlike the `button_click` event, the `button_final_click` event is sent once, at the end of a series of clicks. | `int clicks = system_button_clicks(param); ` retrieves the number of times the button was pushed. | +| 0.6.1 | time_changed | 16384 | device time changed | `time_changed_manually` or `time_changed_sync` | +| 0.6.1 | low_battery | 32768 | generated when low battery condition is detected. | not used | + + +## System Modes + +System modes help you control how the device manages the connection with the cloud. + +By default, the device connects to the Cloud and processes messages automatically. However there are many cases where a user will want to take control over that connection. There are three available system modes: `AUTOMATIC`, `SEMI_AUTOMATIC`, and `MANUAL`. These modes describe how connectivity is handled. +These system modes describe how connectivity is handled and when user code is run. + +System modes must be called before the setup() function. By default, the device is always in `AUTOMATIC` mode. + +### Automatic mode + +The automatic mode of connectivity provides the default behavior of the device, which is that: + +```cpp +SYSTEM_MODE(AUTOMATIC); + +void setup() { + // This won't be called until the device is connected to the cloud +} + +void loop() { + // Neither will this +} +``` + +- When the device starts up, it automatically tries to connect to Wi-Fi and the Particle Cloud. +- Once a connection with the Particle Cloud has been established, the user code starts running. +- Messages to and from the Cloud are handled in between runs of the user loop; the user loop automatically alternates with [`Particle.process()`](#particle-process-). +- `Particle.process()` is also called during any delay() of at least 1 second. +- If the user loop blocks for more than about 20 seconds, the connection to the Cloud will be lost. To prevent this from happening, the user can call `Particle.process()` manually. +- If the connection to the Cloud is ever lost, the device will automatically attempt to reconnect. This re-connection will block from a few milliseconds up to 8 seconds. +- `SYSTEM_MODE(AUTOMATIC)` does not need to be called, because it is the default state; however the user can invoke this method to make the mode explicit. + +In automatic mode, the user can still call `Particle.disconnect()` to disconnect from the Cloud, but is then responsible for re-connecting to the Cloud by calling `Particle.connect()`. + +### Semi-automatic mode + + +The semi-automatic mode will not attempt to connect the device to the Cloud automatically. However once the device is connected to the Cloud (through some user intervention), messages will be processed automatically, as in the automatic mode above. + +```cpp +SYSTEM_MODE(SEMI_AUTOMATIC); + +void setup() { + // This is called immediately +} + +void loop() { + if (buttonIsPressed()) { + Particle.connect(); + } else { + doOfflineStuff(); + } +} +``` + +The semi-automatic mode is therefore much like the automatic mode, except: + +- When the device boots up, `setup()` and `loop()` will begin running immediately. +- Once the user calls [`Particle.connect()`](#particle-connect-), the user code will be blocked between calls of `loop()` while the device attempts to negotiate a connection. This connection will block execution of `loop()` until either the device connects to the Cloud or an interrupt is fired that calls [`Particle.disconnect()`](#particle-disconnect-). + +To block inside `setup()` or `loop()` during the connection attempt, use `waitFor`. + +`Particle.connect(); + +waitFor(Particle.connected, 30000);` + +### Manual mode + + +The "manual" mode puts the device's connectivity completely in the user's control. This means that the user is responsible for both establishing a connection to the Particle Cloud and handling communications with the Cloud by calling [`Particle.process()`](#particle-process-) on a regular basis. + +```cpp +SYSTEM_MODE(MANUAL); + +void setup() { + // This will run automatically +} + +void loop() { + if (buttonIsPressed()) { + Particle.connect(); + } + if (Particle.connected()) { + Particle.process(); + doOtherStuff(); + } +} +``` + +When using manual mode: + +- The user code will run immediately when the device is powered on. +- Once the user calls [`Particle.connect()`](#particle-connect-), the device will attempt to begin the connection process. +- Once the device is connected to the Cloud ([`Particle.connected()`](#particle-connected-)` == true`), the user must call `Particle.process()` regularly to handle incoming messages and keep the connection alive. The more frequently `Particle.process()` is called, the more responsive the device will be to incoming messages. +- If `Particle.process()` is called less frequently than every 20 seconds, the connection with the Cloud will die. It may take a couple of additional calls of `Particle.process()` for the device to recognize that the connection has been lost. + + +{{#if has-threading}} +## System Thread + +_Since 0.4.6_ + +{{#if electron}}**Please note:** The System Thread feature is in Beta - we advise only using this +in production after extensive testing.{{/if}} + +The System Thread is a system configuration that helps ensure the application loop +is not interrupted by the system background processing and network management. +It does this by running the application loop and the system loop on separate threads, +so they execute in parallel rather than sequentially. + +At present, System Thread is an opt-in change. To enable system threading for your application, add to the top of your application code. + +``` +// EXAMPLE USAGE +SYSTEM_THREAD(ENABLED); +``` + + + +### System Threading Behavior + +When the system thread is enabled, application execution changes compared to +non-threaded execution: + +- `setup()` is executed immediately regardless of the system mode, which means +setup typically executes before the Network or Cloud is connected. +Calls to network-related code will be impacted and may fail because the network is not up yet. +`Particle.function()`, `Particle.variable()` and `Particle.subscribe()` will function +as intended whether the cloud is connected or not. `Particle.publish()` will return +`false` when the cloud is not available and the event will not be published. +Other network initialisation (such as those in `UDP`, `TCPServer` and `TCPClient`) +may not function yet. +See `waitUntil` below for details on waiting for the network or cloud connection. + +- after `setup()` is called, `loop()` is called repeatedly, independent from the current state of the +network or cloud connection. The system does not block `loop()` waiting +for the network or cloud to be available, nor while connecting to Wi-Fi. + +- System modes `SEMI_AUTOMATIC` and `MANUAL` behave identically - both of these +modes do not not start the Networking or a Cloud +connection automatically. while `AUTOMATIC` mode connects to the cloud as soon as possible. +Neither has an effect on when the application `setup()` function is run - it is run +as soon as possible, independently from the system network activities, as described above. + +- `Particle.process()` and `delay()` are not needed to keep the background tasks active - they run independently. These functions have a new role in keeping the application events serviced. Application events are: + - cloud function calls + - cloud events + - system events + +- Cloud functions registered with `Particle.function()` and event handlers +registered with `Particle.subscribe()` continue to execute on the application +thread in between calls to `loop()`, or when `Particle.process()` or `delay()` is called. +A long running cloud function will block the application loop (since it is application code) +but not the system code, so cloud connectivity is maintained. + + - the application continues to execute during listening mode + - the application continues to execute during OTA updates + +### System Functions + +With system threading enabled, the majority of the Particle API continues to run on the calling thread, as it does for non-threaded mode. For example, when a function, such as `Time.now()`, is called, it is processed entirely on the calling thread (typically the application thread when calling from `loop()`.) + +There are a small number of API functions that are system functions. These functions execute on the system thread regardless of which thread they are called from. + +There are two types of system functions: + +- asynchronous system functions: these functions do not return a result to the caller and do not block the caller +- synchronous system functions: these functions return a result to the caller, and block the caller until the function completes + +Asynchronous system functions do not block the application thread, even when the system thread is busy, so these can be used liberally without causing unexpected delays in the application. (Exception: when more than 20 asynchronous system functions are invoked, but not yet serviced by the application thread, the application will block for 5 seconds while attempting to put the function on the system thread queue.) + +Synchronous system functions always block the caller until the system has performed the requested operation. These are the synchronous system functions: + +- `WiFi.hasCredentials()`, `WiFi.setCredentials()`, `WiFi.clearCredentials()` +- `Particle.function()` +- `Particle.variable()` +- `Particle.subscribe()` +- `Particle.publish()` + +For example, when the system is busy connecting to Wi-Fi or establishing the cloud connection and the application calls `Particle.variable()` then the application will be blocked until the system finished connecting to the cloud (or gives up) so that it is free to service the `Particle.variable()` function call. + +This presents itself typically in automatic mode and where `setup()` registers functions, variables or subscriptions. Even though the application thread is running `setup()` independently of the system thread, calling synchronous functions will cause the application to block until the system thread has finished connecting to the cloud. This can be avoided by delaying the cloud connection until after the synchronous functions have been called. + +``` +SYSTEM_THREAD(ENABLED); +SYSTEM_MODE(SEMI_AUTOMATIC); + +void setup() +{ + // the system thread isn't busy so these synchronous functions execute quickly + Particle.subscribe("event", handler); + Particle.publish("myvar", myvar); + Particle.connect(); // <-- now connect to the cloud, which ties up the system thread +} +``` + +### Task Switching + +The system firmware includes an RTOS (Real Time Operating System). The RTOS is responsible for switching between the application thread and the system thread, which it does automatically every millisecond. This has 2 main consequences: + +- delays close to 1ms are typically much longer +- application code may be stopped at any time when the RTOS switches to the system thread + +When executing timing-critical sections of code, the task switching needs to be momentarily disabled. + +### SINGLE_THREADED_BLOCK() + +`SINGLE_THREADED_BLOCK()` declares that the next code block is executed in single threaded mode. Task switching is disabled until the end of the block and automatically re-enabled when the block exits. Interrupts remain enabled, so the thread may be interrupted for small periods of time, such as by interrupts from peripherals. + +``` +// SYNTAX +SINGLE_THREADED_BLOCK() { + // code here is executed atomically, without task switching + // or interrupts +} +``` + +Here's an example: + +``` +void so_timing_sensitive() +{ + if (ready_to_send) { + SINGLE_THREADED_BLOCK() { // single threaded execution starts now + digitalWrite(D0, LOW); // timing critical GPIO + delayMicroseconds(1500); + digitalWrite(D0, HIGH); + } + } // single threaded execution stops now +} +``` + + +### ATOMIC_BLOCK() + +`ATOMIC_BLOCK()` is similar to `SINGLE_THREADED_BLOCK()` in that it prevents other threads executing during a block of code. In addition, interrupts are also disabled. + +WARNING: Disabling interrupts prevents normal system operation. Consequently, `ATOMIC_BLOCK()` should be used only for brief periods where atomicity is essential. + +``` +// SYNTAX +ATOMIC_BLOCK() { + // code here is executed atomically, without task switching + // or interrupts +} +``` + +Here's an example: + +``` +void so_timing_sensitive_and_no_interrupts() +{ + if (ready_to_send) { + ATOMIC_BLOCK() { // only this code runs from here on - no other threads or interrupts + digitalWrite(D0, LOW); // timing critical GPIO + delayMicroseconds(1500); + digitalWrite(D0, HIGH); + } + } // other threads and interrupts can run from here +} +``` + +### Synchronizing Access to Shared System Resources + +With system threading enabled, the system thread and the application thread run in parallel. When both attempt to use the same resource, such as writing a message to `Serial`, there is no guaranteed order - the message printed by the system and the message printed by the application are arbitrarily interleaved as the RTOS rapidly switches between running a small part of the system code and then the application code. This results in both messages being intermixed. + +This can be avoided by acquiring exclusive access to a resource. To get exclusive access to a resource, we can use locks. A lock +ensures that only the thread owning the lock can access the resource. Any other thread that tries to use the resource via the lock will not be granted access until the first thread eventually unlocks the resource when it is done. + +At present there is only one shared resource that is used by the system and the application - `Serial`. The system makes use of `Serial` during listening mode. If the application also makes use of serial during listening mode, then it should be locked before use. + +``` +void print_status() +{ + WITH_LOCK(Serial) { + Serial.print("Current status is:"); + Serial.println(status); + } +} +``` + +The primary difference compared to using Serial without a lock is the `WITH_LOCK` declaration. This does several things: + +- attempts to acquire the lock for `Serial`. If the lock isn't available, the thread blocks indefinitely until it is available. + +- once `Serial` has been locked, the code in the following block is executed. + +- when the block has finished executing, the lock is released, allowing other threads to use the resource. + +It's also possible to attempt to lock a resource, but not block when the resource isn't available. + +``` +TRY_LOCK(Serial) { + // this code is only run when no other thread is using Serial +} +``` + +The `TRY_LOCK()` statement functions similarly to `WITH_LOCK()` but it does not block the current thread if the lock isn't available. Instead, the entire block is skipped over. + + +### Waiting for the system + +The [waitUntil](#waituntil-) function can be used to wait for something to happen. +Typically this is waiting for something that the system is doing, +such as waiting for Wi-Fi to be ready or the cloud to be connected. + + +#### waitUntil() + +Sometimes you want your application to wait until the system is in a given state. + +For example, you want to publish a critical event. this can be done using the `waitUntil` function: + +```cpp + // wait for the cloud to be connected + waitUntil(Particle.connected); + bool sent = Particle.publish("weather", "sunny"); +``` + +This will delay the application indefinitely until the cloud is connected. To delay the application +only for a period of time, we can use `waitFor` + +```cpp + // wait for the cloud connection to be connected or timeout after 10 seconds + if (waitFor(Particle.connected, 10000)) { + bool sent = Particle.publish("weather", "sunny"); + } +``` + +`WiFi.ready` is another common event to wait for. + +```cpp + // wait until Wi-Fi is ready + waitUntil(WiFi.ready); +``` + +{{/if}} {{!-- has-threading --}} + +## System Calls + +### version() + +_Since 0.4.7_ + +Determine the version of system firmware available. Returns a version string +of the format: + +> MAJOR.MINOR.PATCH -This tells the system to store these values in RAM so they can be changed. The -system takes care of giving them initial values. Before they are set, -they will have the initial value 0 if an initial value isn't specified. +Such as "0.4.7". -Variables stored in backup RAM follow a similar scheme but use an additional keyword `retained`: +For example -```C++ -// retained variables stored in backup RAM -retained float lastTemperature; -retained int numberOfPresses; -retained int numberOfTriesRemaining = 10; ``` -A `retained` variable is similar to a regular variable, with some key differences: +void setup() +{ + Serial.printlnf("System version: %s", System.version().c_str()); + // prints + // System version: 0.4.7 +} -- it is stored in backup RAM - no space is used in regular RAM -- instead of being initialized on each program start, `retained` variables are initialized -when the device is first powered on (with VIN, from being powered off with VIN and VBAT completely removed). -When the device is powered on, the system takes care of setting these variables to their initial values. -`lastTemperature` and `numberOfPresses` would be initialized to 0, while `numberOfTriesRemaining` would be initialized to 10. -- the last value set on the variable is retained *as long as the device is powered from VIN or VBAT and is not hard reset*. +``` -`retained` variables can be updated freely just as with regular RAM variables and operate -just as fast as regular RAM variables. +### versionNumber() -Here's some typical use cases for `retained` variables: +Determines the version of system firmware available. Returns the version encoded +as a number: -- storing data for use after waking up from deep sleep -- storing data for use after power is removed on VIN, while power is still applied to VBAT (with coin cell battery or super capacitor) -- storing data for use after a hardware or software reset +> 0xAABBCCDD -Finally, if you don't need the persistence of `retained` variables, you -can consider them simply as 4KB of extra RAM to use. + - `AA` is the major release + - `BB` is the minor release + - `CC` is the patch number + - `DD` is 0 -```C++ -// EXAMPLE USAGE -STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY)); +Firmware 0.4.7 has a version number 0x00040700 -retained int value = 10; -void setup() { - Serial.begin(9600); +### buttonPushed() + +_Since 0.4.6_ + +Can be used to determine how long the System button (MODE on Core/Electron, SETUP on Photon) has been pushed. + +Returns `uint16_t` as duration button has been held down in milliseconds. + +```C++ +// EXAMPLE USAGE +void button_handler(system_event_t event, int duration, void* ) +{ + if (!duration) { // just pressed + RGB.control(true); + RGB.color(255, 0, 255); // MAGENTA + } + else { // just released + RGB.control(false); + } } -void loop() { - Serial.println(value); - value = 20; - Serial.println(value); - delay(100); // Give the serial TX buffer a chance to empty - System.sleep(SLEEP_MODE_DEEP, 10); - // Or try a software reset - // System.reset(); +void setup() +{ + System.on(button_status, button_handler); } -/* OUTPUT - * - * 10 - * 20 - * DEEP SLEEP for 10 seconds - * 20 (value is retained as 20) - * 20 - * - */ +void loop() +{ + // it would be nice to fire routine events while + // the button is being pushed, rather than rely upon loop + if (System.buttonPushed() > 1000) { + RGB.color(255, 255, 0); // YELLOW + } +} ``` -### Enabling Backup RAM (SRAM) -Backup RAM is disabled by default, since it does require some maintenance power -which may not be desired on some low-powered projects. Backup RAM consumes roughly -5uA or less on VIN and 9uA or less on VBAT. +### System Cycle Counter -Backup RAM is enabled with this code (to be placed at the top of your application outside of any functions): +_Since 0.4.6_ + +The system cycle counter is incremented for each instruction executed. It functions +in normal code and during interrupts. Since it operates at the clock frequency +of the device, it can be used for accurately measuring small periods of time. ```cpp + // overview of System tick functions + uint32_t now = System.ticks(); -STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY)); + // for converting an the unknown system tick frequency into microseconds + uint32_t scale = System.ticksPerMicrosecond(); + // delay a given number of ticks. + System.ticksDelay(10); ``` -### Making changes to the layout or types of retained variables +The system ticks are intended for measuring times from less than a microsecond up +to a second. For longer time periods, using [micros()](#micros-) or [millis()](#millis-) would +be more suitable. -When adding new `retained` variables to an existing set of `retained` variables, -it's a good idea to add them after the existing variables. this ensures the -existing retained data is still valid even with the new code. -For example, if we wanted to add a new variable `char name[50]` we should add this after -the existing `retained` variables: +#### ticks() + +Returns the current value of the system tick count. One tick corresponds to +one cpu cycle. + +```cpp + // measure a precise time whens something start + uint32_t ticks = System.ticks(); ``` -retained float lastTemperature; -retained int numberOfPresses; -retained int numberOfTriesRemaining = 10; -retained char name[50]; + +#### ticksPerMicrosecond(); + +Retrieves the number of ticks per microsecond for this device. This is useful +when converting between a number of ticks and time in microseconds. + +```cpp + + uint32_t start = System.ticks(); + startTheFrobnicator(); + uint32_t end = System.ticks(); + uint32_t duration = (end-start)/System.ticksPerMicrosecond(); + + Serial.printlnf("The frobnicator took %d microseconds to start", duration); + ``` -If instead we added `name` to the beginning or middle of the block of variables, -the program would end up reading the stored values of the wrong variables. This is -because the new code would be expecting to find the variables in a different memory location. +#### ticksDelay() -Similarly, you should avoid changing the type of your variables as this will also -alter the memory size and location of data in memory. +Pause execution a given number of ticks. This can be used to implement precise +delays. -This caveat is particularly important when updating firmware without power-cycling -the device, which uses a software reset to reboot the device. This will allow previously -`retained` variables to persist. +```cpp + // delay 10 ticks. How long this is actually depends upon the clock speed of the + // device. + System.ticksDelay(10); -During development, a good suggestion to avoid confusion is to design your application to work -correctly when power is being applied for the first time, and all `retained` variables are -initialized. If you must rearrange variables, simply power down the device (VIN and VBAT) -after changes are made to allow reinitialization of `retained` variables on the next power -up of the device. + // to delay for 3 microseconds on any device: + System.ticksDelay(3*System.ticksPerMicrosecond()); -It's perfectly fine to mix regular and `retained` variables, but for clarity we recommend -keeping the `retained` variables in their own separate block. In this way it's easier to recognize -when new `retained` variables are added to the end of the list, or when they are rearranged. +``` +The system code has been written such that the compiler can compute the number +of ticks to delay +at compile time and inline the function calls, reducing overhead to a minimum. -{{/unless}} -## Macros -### STARTUP() +### freeMemory() -_Since 0.4.5_ +_Since 0.4.4_ -Typically an application will have its initialization code in the `setup()` function. -This works well if a delay of a few seconds from power on/reset is acceptable. +Retrieves the amount of free memory in the system in bytes. -In other cases, the application wants to have code run as early as possible, before the cloud or network connection -are initialized. The `STARTUP()` function instructs the system to execute the code early on in startup. +```cpp +uint32_t freemem = System.freeMemory(); +Serial.print("free memory: "); +Serial.println(freemem); +``` + + +{{#if core}} +### factoryReset() + +This will perform a factory reset and do the following: +- Restore factory reset firmware from external flash (tinker) +- Erase Wi-Fi profiles +- Enter Listening mode upon completion ```cpp -void setup_the_fundulating_conbobulator() +System.factoryReset() +``` +{{/if}} +### dfu() + +The device will enter DFU-mode to allow new user firmware to be refreshed. DFU mode is cancelled by +- flashing firmware to the device using dfu-util, specifying the `:leave` option, or +- a system reset + +```cpp +System.dfu() +``` + +To make DFU mode permanent - so that it continues to enter DFU mode even after a reset until +new firmware is flashed, pass `true` to the `dfu()` function. + +```cpp +System.dfu(true); // persistent DFU mode - will enter DFU after a reset until firmware is flashed. +``` + + +### deviceID() + +`System.deviceID()` provides an easy way to extract the device ID of your device. It returns a [String object](#string-class) of the device ID, which is used to identify your device. + +```cpp +// EXAMPLE USAGE + +void setup() { - pinMode(D3, OUTPUT); - digitalWrite(D3, HIGH); + // Make sure your Serial Terminal app is closed before powering your device + Serial.begin(9600); + // Now open your Serial Terminal, and hit any key to continue! + while(!Serial.available()) Particle.process(); + + String myID = System.deviceID(); + // Prints out the device ID over Serial + Serial.println(myID); } -// The STARTUP call is placed outside of any other function -// What goes inside is any valid code that can be executed. Here, we use a function call. -// Using a single function is preferable to having several `STARTUP()` calls. -STARTUP( setup_the_fundulating_conbobulator() ); +void loop() {} +``` + +### enterSafeMode() +_Since 0.4.6_ + +```C++ +// SYNTAX +System.enterSafeMode(); ``` -The code referenced by `STARTUP()` is executed very early in the startup sequence, so it's best suited -to initializing digital I/O and peripherals. Networking setup code should still be placed in `setup()`. -{{#if photon}} -Although there is one notable exception - `WiFi.selectAntenna()` should be called from `STARTUP()` to select the default antenna before the Wi-Fi connection is made. -{{/if}} -_Note that when startup code performs digital I/O, there will still be a period of at least few hundred milliseconds -where the I/O pins are in their default power-on state, namely `INPUT`. Circuits should be designed with this -in mind, using pullup/pulldown resistors as appropriate._ +Resets the device and restarts in safe mode. -## System Events +{{#if has-sleep}} -*Since 0.4.9* +### sleep() [ Sleep ] -### System Events Overview +`System.sleep()` can be used to dramatically improve the battery life of a Particle-powered project by temporarily deactivating the Wi-Fi module, which is by far the biggest power draw. -System events are messages sent by the system and received by application code. They inform the application about changes in the system, such as when the system has entered setup mode, or when an Over-the-Air (OTA) update starts, or when the system is about to reset. +```C++ +// SYNTAX +System.sleep(long seconds); +``` -System events are recieved by the application by registering a handler. The handler has this general format: +```C++ +// EXAMPLE USAGE -``` -void handler(system_event_t event, int data, void* moredata); +// Put the Wi-Fi module in standby (low power) for 5 seconds +System.sleep(5); +// The device LED will breathe white during sleep ``` -Unused parameters can be removed from right to left, giving these additional function signatures: +`System.sleep(long seconds)` does NOT stop the execution of application code (non-blocking call). Application code will continue running while the Wi-Fi module is in standby mode. -``` -void handler(system_event_t event, int data); -void handler(system_event_t event); -void handler(); -``` +`System.sleep(SLEEP_MODE_DEEP, long seconds)` can be used to put the entire device into a *deep sleep* mode. +In this particular mode, the device shuts down the network subsystem and puts the microcontroller in a stand-by mode. +When the device awakens from deep sleep, it will reset and run all user code from the beginning with no values being maintained in memory from before the deep sleep. -Here's an example of an application that listens for `reset` events so that the application is notified the device is about to reset. The application publishes a reset message to the cloud and turns off connected equipment before returning from the handler, allowing the device to reset. +As such, it is recommended that deep sleep be called only after all user code has completed. The Standby mode is used to achieve the lowest power consumption. After entering Standby mode, the SRAM and register contents are lost except for registers in the backup domain. +```C++ +// SYNTAX +System.sleep(SLEEP_MODE_DEEP, long seconds); ``` -void reset_handler() -{ - // turn off the crankenspitzen - digitalWrite(D6, LOW); - // tell the world what we are doing - Particle.publish("reset", "going down for reboot NOW!"); -} -void setup() -{ - // register the reset handler - System.on(reset, reset_handler); -} +```C++ +// EXAMPLE USAGE + +// Put the device into deep sleep for 60 seconds +System.sleep(SLEEP_MODE_DEEP,60); +// The device LED will shut off during deep sleep ``` +The device will automatically *wake up* and reestablish the Wi-Fi connection after the specified number of seconds. -Some event types provide additional information. For example the `button_click` event provides a parameter with the number of button clicks: +**Note:** +You can also wake the device "prematurely" by applying a rising edge signal to the {{#if core}}A7{{/if}}{{#unless core}}WKP{{/unless}} pin. -``` -void button_clicked(system_event_t event, int param) -{ - int times = system_button_clicks(param); - Serial.printlnf("button was clicked %d times", times); -} -``` +`System.sleep(uint16_t wakeUpPin, uint16_t edgeTriggerMode)` can be used to put the entire device into a *stop* mode with *wakeup on interrupt*. In this particular mode, the device shuts down the network and puts the microcontroller in a stop mode with configurable wakeup pin and edge triggered interrupt. When the specific interrupt arrives, the device awakens from stop mode. {{#if core}} On the Core, the Core is reset on entering stop mode and runs all user code from the beginning with no values being maintained in memory from before the stop mode. As such, it is recommended that stop mode be called only after all user code has completed.{{/if}} {{#unless core}}The device will not reset before going into stop mode so all the application variables are preserved after waking up from this mode. The voltage regulator is put in low-power mode. This mode achieves the lowest power consumption while retaining the contents of SRAM and registers.{{/unless}} -#### Registering multiple events with the same handler +{{#if core}} +It is mandatory to update the *bootloader* (https://github.com/spark/firmware/tree/bootloader-patch-update) for proper functioning of this mode. +{{/if}} -It's possible to subscribe to multiple events with the same handler in cases where you want the same handler to be notified for all the events. For example: +{{#if electron}} +The Electron maintains the cellular connection for the duration of the sleep when `SLEEP_NETWORK_STANDBY` is given as the last parameter value. On wakeup, the device is able to reconnect to the cloud much quicker, at the expense of increased power consumption. +{{/if}} +```C++ +// SYNTAX +System.sleep(uint16_t wakeUpPin, uint16_t edgeTriggerMode); +{{#if electron}} +System.sleep(uint16_t wakeUpPin, uint16_t edgeTriggerMode, SLEEP_NETWORK_STANDBY); +{{/if}} ``` -void handle_all_the_events(system_event_t event, int param) -{ - Serial.printlnf("got event %d with value %d"); -} -void setup() -{ - // listen for Wi-Fi Listen events and Firmware Update events - System.on(wifi_listen+firmware_update, handle_all_the_events); -} +```C++ +// EXAMPLE USAGE + +// Put the device into stop mode with wakeup using RISING edge interrupt on D0 pin +System.sleep(D0,RISING); +// The device LED will shut off during sleep ``` -To subscribe to all events, there is the placeholder `all_events`: +*Parameters:* + +- `wakeUpPin`: the wakeup pin number. supports external interrupts on the following pins: + - D0, D1, D2, D3, D4, A0, A1, A3, A4, A5, A6, A7 +- `edgeTriggerMode`: defines when the interrupt should be triggered. Four constants are predefined as valid values: + - CHANGE to trigger the interrupt whenever the pin changes value, + - RISING to trigger when the pin goes from low to high, + - FALLING for when the pin goes from high to low. + +`System.sleep(uint16_t wakeUpPin, uint16_t edgeTriggerMode, long seconds)` can be used to put the entire device into a *stop* mode with *wakeup on interrupt* or *wakeup after specified seconds*. In this particular mode, the device shuts network subsystem and puts the microcontroller in a stop mode with configurable wakeup pin and edge triggered interrupt or wakeup after the specified seconds. When the specific interrupt arrives or upon reaching the configured timeout, the device awakens from stop mode. {{#if core}} On the Core, the Core is reset on entering stop mode and runs all user code from the beginning with no values being maintained in memory from before the stop mode. As such, it is recommended that stop mode be called only after all user code has completed.{{/if}} {{#unless core}}The device will not reset before going into stop mode so all the application variables are preserved after waking up from this mode. The voltage regulator is put in low-power mode. This mode achieves the lowest power consumption while retaining the contents of SRAM and registers.{{/unless}} +```C++ +// SYNTAX +System.sleep(uint16_t wakeUpPin, uint16_t edgeTriggerMode, long seconds{{#if electron}}[, SLEEP_NETWORK_STANDBY]{{/if}}); ``` -void setup() -{ - // listen for network events and firmware update events - System.on(all_events, handle_all_the_events); -} + +```C++ +// EXAMPLE USAGE + +// Put the device into stop mode with wakeup using RISING edge interrupt on D0 pin or wakeup after 60 seconds whichever comes first +System.sleep(D0,RISING,60); +// The device LED will shut off during sleep ``` -### System Events Reference +{{#if core}}On the Core, it is necessary to update the *bootloader* (https://github.com/spark/firmware/tree/bootloader-patch-update) for proper functioning of this mode.{{/if}} -These are the system events produced by the system, their numeric value (what you will see when printing the system event to Serial) and details of how to handle the parameter value. -| Event Name | ID | Description | Parameter | -|------------|----------|-----------| -| setup_begin | 2 | signals the device has entered setup mode | not used | -| setup_update | 4 | periodic event signalling the device is still in setup mode. | milliseconds since setup mode was started | -| setup_end | 8 | signals setup mode was exited | time in ms since setup mode was started | -| network_credentials | 16 | network credentials were changed | `network_credentials_added` or `network_credentials_cleared` | - | button_status | 128 | button pressed or releasesed | the duration in ms the button was pressed: 0 when pressed, >0 on release. | - | firmware_update | 256 | firmwarwe update status | one of `firmware_update_begin`, `firmware_update_progress`, `firmware_update_complete`, `firmware_update_failed` | - | firmware_update_pending | 512 | notifies the application that a firmware update is available. This event is sent even when updates are disabled, giving the application chance to re-enable firmware updates with `System.enableUpdates()` | not used | - | reset_pending | 1024 | notifies the application that the system would like to reset. This event is sent even when resets are disabled, giving the application chance to re-enable resets with `System.enableReset()` | not used | - | reset | 2048 | notifies that the system will reset once the application has completed handling this event | not used | - | button_click | 4096 | event sent each time setup button is clicked. | `int clicks = system_button_clicks(param); ` retrieves the number of clicks so far. | -| button_final_click | 8192 | sent after a run of one or more clicks not followed by additional clicks. Unlike the `button_click` event, the `button_final_click` event is sent once, at the end of a series of clicks. | `int clicks = system_button_clicks(param); ` retrieves the number of times the button was pushed. | +*Parameters:* +- `wakeUpPin`: the wakeup pin number. supports external interrupts on the following pins: + - D0, D1, D2, D3, D4, A0, A1, A3, A4, A5, A6, A7 +- `edgeTriggerMode`: defines when the interrupt should be triggered. Four constants are predefined as valid values: + - CHANGE to trigger the interrupt whenever the pin changes value, + - RISING to trigger when the pin goes from low to high, + - FALLING for when the pin goes from high to low. +- `seconds`: wakeup after the specified number of seconds (0 = no alarm is set) +{{#if electron}} +- `SLEEP_NETWORK_STANDBY`: optional - keeps the cellular modem in a standby state while the device is sleeping.. +{{/if}} -## System Modes +*Power consumption:* -System modes help you control how the device manages the connection with the cloud. +- Core + - In *standard sleep mode*, the device current consumption is in the range of: **30mA to 38mA** + - In *deep sleep mode*, the current consumption is around: **3.2 μA** +- Photon + - Please see the [Photon datasheet](/datasheets/photon-datasheet/#recommended-operating-conditions) -By default, the device connects to the Cloud and processes messages automatically. However there are many cases where a user will want to take control over that connection. There are three available system modes: `AUTOMATIC`, `SEMI_AUTOMATIC`, and `MANUAL`. These modes describe how connectivity is handled. -These system modes describe how connectivity is handled and when user code is run. -System modes must be called before the setup() function. By default, the device is always in `AUTOMATIC` mode. +_Since 0.4.5_ The state of the {{#if has-wifi}}Wi-Fi{{/if}}{{#if has-cellular}}Cellular{{/if}} and Cloud connections is restored when the system wakes up from sleep. So if the device was connected to the cloud before sleeping, then the cloud connection +is automatically resumed on waking up. -### Automatic mode +_Since 0.5.0_ In automatic modes, the `sleep()` function doesn't return until the cloud connection has been established. This means that application code can use the cloud connection as soon as `sleep()` returns. In previous versions, it was necessary to call `Particle.process()` to have the cloud reconnected by the system in the background. -The automatic mode of connectivity provides the default behavior of the device, which is that: +{{/if}} {{!-- has-sleep --}} -```cpp -SYSTEM_MODE(AUTOMATIC); +### reset() + +Resets the device, just like hitting the reset button or powering down and back up. + +```C++ +uint32_t lastReset = 0; void setup() { - // This won't be called until the device is connected to the cloud + lastReset = millis(); } void loop() { - // Neither will this + // Reset after 5 minutes of operation + // ================================== + if (millis() - lastReset > 5*60000UL) { + System.reset(); + } } ``` -- When the device starts up, it automatically tries to connect to Wi-Fi and the Particle Cloud. -- Once a connection with the Particle Cloud has been established, the user code starts running. -- Messages to and from the Cloud are handled in between runs of the user loop; the user loop automatically alternates with [`Particle.process()`](#particle-process-). -- `Particle.process()` is also called during any delay() of at least 1 second. -- If the user loop blocks for more than about 20 seconds, the connection to the Cloud will be lost. To prevent this from happening, the user can call `Particle.process()` manually. -- If the connection to the Cloud is ever lost, the device will automatically attempt to reconnect. This re-connection will block from a few milliseconds up to 8 seconds. -- `SYSTEM_MODE(AUTOMATIC)` does not need to be called, because it is the default state; however the user can invoke this method to make the mode explicit. - -In automatic mode, the user can still call `Particle.disconnect()` to disconnect from the Cloud, but is then responsible for re-connecting to the Cloud by calling `Particle.connect()`. - -### Semi-automatic mode - +### disableReset() -The semi-automatic mode will not attempt to connect the device to the Cloud automatically. However once the device is connected to the Cloud (through some user intervention), messages will be processed automatically, as in the automatic mode above. +This method allows to disable automatic resetting of the device on such events as successful firmware update. ```cpp -SYSTEM_MODE(SEMI_AUTOMATIC); +// EXAMPLE +void on_reset_pending() { + // Enable resetting of the device. The system will reset after this method is called + System.enableReset(); +} void setup() { - // This is called immediately + // Register the event handler + System.on(reset_pending, on_reset_pending); + // Disable resetting of the device + System.enableReset(); + } void loop() { - if (buttonIsPressed()) { - Particle.connect(); - } else { - doOfflineStuff(); - } } ``` -The semi-automatic mode is therefore much like the automatic mode, except: +When the system needs to reset the device it first sends the [`reset_pending`](#system-events-reference) event to the application, and, if automatic resetting is disabled, waits until the application has called `enableReset()` to finally perform the reset. This allows the application to perform any necessary cleanup before resetting the device. -- When the device boots up, `setup()` and `loop()` will begin running immediately. -- Once the user calls [`Particle.connect()`](#particle-connect-), the user code will be blocked between calls of `loop()` while the device attempts to negotiate a connection. This connection will block execution of `loop()` until either the device connects to the Cloud or an interrupt is fired that calls [`Particle.disconnect()`](#particle-disconnect-). +### enableReset() -To block inside `setup()` or `loop()` during the connection attempt, use `waitFor`. +Allows the system to reset the device when necessary. -`Particle.connect(); +### resetPending() -waitFor(Particle.connected, 30000);` +Returns `true` if the system needs to reset the device. -### Manual mode +### Reset Reason +_Since 0.6.0_ -The "manual" mode puts the device's connectivity completely in the user's control. This means that the user is responsible for both establishing a connection to the Particle Cloud and handling communications with the Cloud by calling [`Particle.process()`](#particle-process-) on a regular basis. +The system can track the hardware and software resets of the device. -```cpp -SYSTEM_MODE(MANUAL); +``` +// EXAMPLE +// Restart in safe mode if the device previously reset due to a PANIC (SOS code) +STARTUP(System.enableFeature(FEATURE_RESET_INFO)); void setup() { - // This will run automatically + if (System.resetReason() == RESET_REASON_PANIC) { + System.enterSafeMode(); + } } +``` -void loop() { - if (buttonIsPressed()) { - Particle.connect(); - } - if (Particle.connected()) { - Particle.process(); - doOtherStuff(); - } +You can also pass in your own data as part of an application-initiated reset: + +```cpp +// EXAMPLE +STARTUP(System.enableFeature(FEATURE_RESET_INFO)); + +void setup() { + // Reset the device 3 times in a row + if (System.resetReason() == RESET_REASON_USER) { + uint32_t data = System.resetReasonData(); + if (data < 3) { + System.reset(data + 1); + } + } else { + // This will set the reset reason to RESET_REASON_USER + System.reset(1); + } } + ``` -When using manual mode: +**Note:** This functionality requires `FEATURE_RESET_INFO` flag to be enabled in order to work. -- The user code will run immediately when the device is powered on. -- Once the user calls [`Particle.connect()`](#particle-connect-), the device will attempt to begin the connection process. -- Once the device is connected to the Cloud ([`Particle.connected()`](#particle-connected-)` == true`), the user must call `Particle.process()` regularly to handle incoming messages and keep the connection alive. The more frequently `Particle.process()` is called, the more responsive the device will be to incoming messages. -- If `Particle.process()` is called less frequently than every 20 seconds, the connection with the Cloud will die. It may take a couple of additional calls of `Particle.process()` for the device to recognize that the connection has been lost. +`resetReason()` +Returns a code describing reason of the last device reset. The following codes are defined: -{{#unless core}} -## System Thread +- `RESET_REASON_PIN_RESET`: Reset button or reset pin +- `RESET_REASON_POWER_MANAGEMENT`: Low-power management reset +- `RESET_REASON_POWER_DOWN`: Power-down reset +- `RESET_REASON_POWER_BROWNOUT`: Brownout reset +- `RESET_REASON_WATCHDOG`: Hardware watchdog reset +- `RESET_REASON_UPDATE`: Successful firmware update +- `RESET_REASON_UPDATE_TIMEOUT`: Firmware update timeout +- `RESET_REASON_FACTORY_RESET`: Factory reset requested +- `RESET_REASON_SAFE_MODE`: Safe mode requested +- `RESET_REASON_DFU_MODE`: DFU mode requested +- `RESET_REASON_PANIC`: System panic +- `RESET_REASON_USER`: User-requested reset +- `RESET_REASON_UNKNOWN`: Unspecified reset reason +- `RESET_REASON_NONE`: Information is not available -*Since 0.4.6.* +`resetReasonData()` -{{#if electron}}**Please note:** The System Thread feature is in Beta - we advise only using this -in production after extensive testing.{{/if}} +Returns a user-defined value that has been previously specified for the `System.reset()` call. -The System Thread is a system configuration that helps ensure the application loop -is not interrupted by the system background processing and network management. -It does this by running the application loop and the system loop on separate threads, -so they execute in parallel rather than sequentially. +`reset(uint32_t data)` -At present, System Thread is an opt-in change. To enable system threading for your application, add +This overloaded method accepts an arbitrary 32-bit value, stores it to the backup register and resets the device. The value can be retrieved via `resetReasonData()` method after the device has restarted. -``` -SYSTEM_THREAD(ENABLED); -``` +### System Flags -to the top of your application code. +The system allows to alter certain aspects of its default behavior via the system flags. The following system flags are defined: + * `SYSTEM_FLAG_PUBLISH_RESET_INFO` : enables publishing of the last [reset reason](#reset-reason) to the cloud (enabled by default) + * `SYSTEM_FLAG_RESET_NETWORK_ON_CLOUD_ERRORS` : enables resetting of the network connection on cloud connection errors (enabled by default) -### System Threading Behavior +```cpp +// EXAMPLE +// Do not publish last reset reason +System.disable(SYSTEM_FLAG_PUBLISH_RESET_INFO); -When the system thread is enabled, application execution changes compared to -non-threaded execution: +// Do not reset network connection on cloud errors +System.disable(SYSTEM_FLAG_RESET_NETWORK_ON_CLOUD_ERRORS); +``` -- `setup()` is executed immediately regardless of the system mode, which means -setup typically executes before the Network or Cloud is connected. -Calls to network-related code will be impacted and may fail because the network is not up yet. -`Particle.function()`, `Particle.variable()` and `Particle.subscribe()` will function -as intended whether the cloud is connected or not. `Particle.publish()` will return -`false` when the cloud is not available and the event will not be published. -Other network initialisation (such as those in `UDP`, `TCPServer` and `TCPClient`) -may not function yet. -See `waitUntil` below for details on waiting for the network or cloud connection. +`System.enable(system_flag_t flag)` -- after `setup()` is called, `loop()` is called repeatedly, independent from the current state of the -network or cloud connection. The system does not block `loop()` waiting -for the network or cloud to be available, nor while connecting to Wi-Fi. +Enables the system flag. -- System modes `SEMI_AUTOMATIC` and `MANUAL` behave identically - both of these -modes do not not start the Networking or a Cloud -connection automatically. while `AUTOMATIC` mode connects to the cloud as soon as possible. -Neither has an affect on when the application `setup()` function is run - it is run -as soon as possible, independently from the system network activities, as described above. +`System.disable(system_flag_t flag)` -- `Particle.process()` and `delay()` are not needed to keep the background tasks active - they run independently. These functions have a new role in keeping the application events serviced. Application events are: - - cloud function calls - - cloud events - - system events +Disables the system flag. -- Cloud functions registered with `Particle.function()` and event handlers -registered with `Particle.subscribe()` continue to execute on the application -thread in between calls to `loop()`, or when `Particle.process()` or `delay()` is called. -A long running cloud function will block the application loop (since it is application code) -but not the system code, so cloud connectivity is maintained. +`System.enabled(system_flag_t flag)` - - the application continues to execute during listening mode - - the application continues to execute during OTA updates +Returns `true` if the system flag is enabled. -### System Functions +{{#if has-linux}} -With system threading enabled, the majority of the Particle API continues to run on the calling thread, as it does for non-threaded mode. For example, when a function, such as `Time.now()`, is called, it is processed entirely on the calling thread (typically the application thread when calling from `loop()`.) +## Process Control -There are a small number of API functions that are system functions. These functions execute on the system thread regardless of which thread they are called from. +You can call scripts and run other programs from the firmware. In Linux, a running program is called a process. -There are two types of system functions: +*This interface is in beta. It might change in non-backwards compatible ways.* -- asynchronous system functions: these functions do not return a result to the caller and do not block the caller -- synchronous system functions: these functions return a result to the caller, and block the caller until the function completes +### run() -Asynchronous system functions do not block the application thread, even when the system thread is busy, so these can be used liberally without causing unexpected delays in the application. (Exception: when more than 20 asynchronous system functions are invoked, but not yet serviced by the application thread, the application will block for 5 seconds while attempting to put the function on the system thread queue.) +Start running another program in the background. It returns a `Process` object so you can interact with the program while it running and after it has exited. -Synchronous system functions always block the caller until the system has performed the requested operation. These are the synchronous system functions: +The `command` argument should start with the name of a program or script (with or without path) and can contain other arguments separated by spaces. -- `WiFi.hasCredentials()`, `WiFi.setCredentials()`, `WiFi.clearCredentials()` -- `Particle.function()` -- `Particle.variable()` -- `Particle.subscribe()` -- `Particle.publish()` +The command is executed through the shell: `/bin/sh -c ` -For example, when the system is busy connecting to Wi-Fi or establishing the cloud connection and the application calls `Particle.variable()` then the application will be blocked until the system finished connecting to the cloud (or gives up) so that it is free to service the `Particle.variable()` function call. +```cpp +// SYNTAX +Process proc = Process::run(command) -This presents itself typically in automatic mode and where `setup()` registers functions, variables or subscriptions. Even though the application thread is running `setup()` independently of the system thread, calling synchronous functions will cause the application to block until the system thread has finished connecting to the cloud. This can be avoided by delaying the cloud connection until after the synchronous functions have been called. +// EXAMPLE USAGE +// Simple script and block it is finished +Process proc = Process::run("/home/pi/script.sh"); +proc.wait(); +// Take a picture with a Pi camera +Process proc = Process::run("raspistill -o /home/pi/photo.jpg"); +proc.wait(); ``` -SYSTEM_THREAD(ENABLED); -SYSTEM_MODE(SEMI_AUTOMATIC); -void setup() -{ - // the system thread isn't busy so these synchronous functions execute quickly - Particle.subscribe("event", handler); - Particle.publish("myvar", myvar); - Particle.connect(); // <-- now connect to the cloud, which ties up the system thread -} -``` +It's important to call `wait()` to block the firmware until the program finishes running or call `exited()` until it returns true. Otherwise when the program completes the operating system will keep information about the process in memory forever, eventually making it impossible to start any new process on the entire device. -### Task Switching +### wait() -The system firmware includes an RTOS (Real Time Operating System). The RTOS is responsible for switching between the application thread and the system thread, which it does automatically every millisecond. This has 2 main consequences: +Block the firmware until the program finishes. Returns immediately if the process has already finished. -- delays close to 1ms are typically much longer -- application code may be stopped at any time when the RTOS switches to the system thread +Returns the [exit code of the process](#exitcode-). -When executing timing-critical sections of code, the task switching needs to be momentarily disabled. +```cpp +// SYNTAX +process.wait(); -### SINGLE_THREADED_BLOCK() +// EXAMPLE USAGE +// Run a Javascript program +Process proc = Process::run("node /home/pi/update.js"); +proc.wait(); +``` -`SINGLE_THREADED_BLOCK()` declares that the next code block is executed in single threaded mode. Task switching is disabled until the end of the block and automatically re-enabled when the block exits. Interrupts remain enabled, so the thread may be interrupted for small periods of time, such as by interrupts from peripherals. +### exited() -``` +Returns true if the process has exited, false otherwise. + +A "blank" Process that was never started returns true for `exited()`. + +```cpp // SYNTAX -SINGLE_THREADED_BLOCK() { - // code here is executed atomically, without task switching - // or interrupts +bool done = process.exited(); + +// EXAMPLE USAGE +// Blink an LED during a long operation +Process proc = Process::run("updatedb"); +pinMode(D7, OUTPUT); +while (!proc.exited()) { + digitalWrite(D7, HIGH); + delay(100); + digitalWrite(D7, LOW); + delay(100); } -``` -Here's an example: +// Restart a server when it crashes +Process proc; -``` -void so_timing_sensitive() -{ - if (ready_to_send) { - SINGLE_THREADED_BLOCK() { // single threaded execution starts now - digitalWrite(D0, LOW); // timing critical GPIO - delayMicroseconds(1500); - digitalWrite(D0, HIGH); - } - } // single threaded execution stops now +void loop() { + if (proc.exited()) { + proc = Process::run("node /home/pi/server.js"); + } } ``` +### kill() -### ATOMIC_BLOCK() - -`ATOMIC_BLOCK()` is similar to `SINGLE_THREADED_BLOCK()` in that it prevents other threads executing during a block of code. In addition, interrupts are also disabled. - -WARNING: Disabling interrupts prevents normal system operation. Consequently, `ATOMIC_BLOCK()` should be used only for brief periods where atomicity is essential. +Stop the process by sending a signal. Defaults to the `SIGTERM` signal which asks the program to quit. To force-quit an unresponsive process, use `SIGKILL`. -``` +```cpp // SYNTAX -ATOMIC_BLOCK() { - // code here is executed atomically, without task switching - // or interrupts -} +process.kill(); +process.kill(signal); + +// EXAMPLE USAGE +// Stop a long operation early +Process proc = Process::run("sleep 10"); +proc.kill(); +proc.wait(); ``` -Here's an example: +`signal` is either a signal number or name. Here are the most useful signals. -``` -void so_timing_sensitive_and_no_interrupts() -{ - if (ready_to_send) { - ATOMIC_BLOCK() { // only this code runs from here on - no other threads or interrupts - digitalWrite(D0, LOW); // timing critical GPIO - delayMicroseconds(1500); - digitalWrite(D0, HIGH); - } - } // other threads and interrupts can run from here -} -``` +| Signal Name | Signal Number | Description | +|-------------|---------------|-------------| +| SIGINT | 2 | Interrupt from keyboard (Ctrl-C) | +| SIGABRT | 6 | Abort. Usually from uncaught C++ exception | +| SIGKILL | 9 | Force quit | +| SIGSEGV | 11 | Bad memory operation (null pointer, bad pointer) | +| SIGTERM | 15 | Graceful quit | -### Synchronizing Access to Shared System Resources +It's important to still call `wait()` or `exited()` after `kill()` to ensure the process information is recycled by the operating system. -With system threading enabled, the system thread and the application thread run in parallel. When both attempt to use the same resource, such as writing a message to `Serial`, there is no guaranteed order - the message printed by the system and the message printed by the application are arbitrarily interleaved as the RTOS rapidly switches between running a small part of the system code and then the application code. This results in both messages being intermixed. +### exitCode() -This can be avoided by acquiring exclusive access to a resource. To get exclusive access to a resource, we can use locks. A lock -ensures that only the thread owning the lock can access the resource. Any other thread that tries to use the resource via the lock will not be granted access until the first thread eventually unlocks the resource when it is done. +If the process has exited, returns the integer exit code. -At present there is only one shared resource that is used by the system and the application - `Serial`. The system makes use of `Serial` during listening mode. If the application also makes use of serial during listening mode, then it should be locked before use. +```cpp +// SYNTAX +uint8_t code = proccess.exitCode(); -``` -void print_status() -{ - WITH_LOCK(Serial) { - Serial.print("Current status is:"); - Serial.println(status); - } +// EXAMPLE USAGE +// Did the program finish sucessfully? +Process proc = Process::run("/home/pi/script.sh"); +proc.wait(); +if (proc.exitCode() == 0) { + Serial.println("Success!"); +} + +// Did the program crash? +Process proc = Process::run("my_program"); +proc.wait(); +uint8_t code = proc.exitCode(); +if (code >= 128) { + Serial.printlnf("my_program crashed with signal %d", code - 128); } ``` -The primary difference compared to using Serial without a lock is the `WITH_LOCK` declaration. This does several things: +An exit code of 0 means success. The meaning of non-zero error codes are specific to each program. -- attempts to acquire the lock for `Serial`. If the lock isn't available, the thread blocks indefinitely until it is available. +If a process exits because of a signal, for example it crashed with a bad pointer, the exit code will be 128 plus the signal value. See the table above for the signal values. -- once `Serial` has been locked, the code in the following block is executed. +### out() +### err() -- when the block has finished executing, the lock is released, allowing other threads to use the resource. +The output generated by a program is available through the `out()` and `err()` Stream for standard output and standard error. -It's also possible to attempt to lock a resource, but not block when the resource isn't available. +```cpp +// SYNTAX +process.out(); +process.err(); -``` -TRY_LOCK(Serial) { - // this code is only run when no other thread is using Serial -} +// EXAMPLE USAGE +// Get entire output of program +Process proc = Process::run("ls /home/pi"); +proc.wait(); +String filenames = proc.out().readString(); + +// Get CPU temperature +Process proc = Process::run("vcgencmd measure_temp"); +proc.wait(); +// The output is temp=43.5'C, so read past the = and parse the number +proc.out().find("="); +float cpuTemp = proc.out().parseFloat(); ``` -The `TRY_LOCK()` statement functions similarly to `WITH_LOCK()` but it does not block the current thread if the lock isn't available. Instead, the entire block is skipped over. +All the [Stream](#stream-class) functions are available like `readStringUntil('\n')` to read a line or `parseInt()` to turn the output into an integer. +### in() -### Waiting for the system +To provide input to the program, print to `in()`. -The [waitUntil](#waituntil-) function can be used to wait for something to happen. -Typically this is waiting for something that the system is doing, -such as waiting for Wi-Fi to be ready or the cloud to be connected. +```cpp +// SYNTAX +process.in(); +// EXAMPLE USAGE +// Run a calculation using the bc, a calculator program +Process proc = Process::run("bc"); +proc.in().println("6 * 7"); +proc.in().close(); // <-- THIS IS IMPORTANT +proc.wait(); +int result = proc.out().parseInt(); // 42 +``` -#### waitUntil() +The same functions used to print to `Serial` like `println` and `printf` are available. -Sometimes you want your application to wait until the system is in a given state. +**Note**: It is very important to close `in()` so the process knows that no further input is coming. If you don't do this, the process will hang forever waiting for more input. -For example, you want to publish a critical event. this can be done using the `waitUntil` function: +### Advanced Process Control + +Linux process control is a deep topic on its own. If the methods in `Process` don't work for what you're trying to accomplish, you can also use any Linux process control functions like `system`, `fork` and `execve` method directly in your firmware. ```cpp - // wait for the cloud to be connected - waitUntil(Particle.connected); - bool sent = Particle.publish("weather", "sunny"); +// Run a command using the Linux system() function instead of Process +// The output won't be available +system("my_command"); ``` -This will delay the application indefinitely until the cloud is connected. To delay the application -only for a period of time, we can use `waitFor` +{{/if}} {{!-- has-linux --}} -```cpp - // wait for the cloud connection to be connected or timeout after 10 seconds - if (waitFor(Particle.connected, 10000)) { - bool sent = Particle.publish("weather", "sunny"); - } +{{#unless core}} +### buttonMirror() + +_Since 0.6.1_ + +Allows a pin to mirror the functionality of the SETUP/MODE button. + +```C++ +// SYNTAX +System.buttonMirror(D1, RISING); +System.buttonMirror(D1, FALLING, true); ``` +Parameters: -`WiFi.ready` is another common event to wait for. + * `pin`: the pin number + * `mode`: defines the condition that signifies a button press: + - RISING to trigger when the pin goes from low to high, + - FALLING for when the pin goes from high to low. + * `bootloader`: (optional) if `true`, the mirror pin configuration is saved in DCT and pin mirrors the SETUP/MODE button functionality while in bootloader as well. If `false`, any previously stored configuration is removed from the DCT and pin only mirrors the SETUP/MODE button while running the firmware (default). + +See also [`System.disableButtonMirror()`](#disablebuttonmirror-). ```cpp - // wait until Wi-Fi is ready - waitUntil(WiFi.ready); +// EXAMPLE +// Mirror SETUP/MODE button on D1 pin. Button pressed state - LOW +STARTUP(System.buttonMirror(D1, FALLING)); + +// EXAMPLE +// Mirror SETUP/MODE button on D1 pin. Button pressed state - HIGH +// Works in both firmware and bootloader +STARTUP(System.buttonMirror(D1, RISING, true)); ``` +***NOTE:*** Pins `D0` and `A5` will disable normal SETUP button operation. Pins `D0` and `A5` also can not be used in bootloader, the configuration will not be saved in DCT. + +### disableButtonMirror() + +_Since 0.6.1_ + +Disables SETUP button mirroring on a pin. + +```C++ +// SYNTAX +System.disableButtonMirror(); +System.disableButtonMirror(false); +``` +Parameters: + * `bootloader`: (optional) if `true`, the mirror pin configuration is cleared from the DCT, disabling the feature in bootloader (default). + {{/unless}} -## System Calls +## OTA Updates -### version() +Application firmware can use these functions to turn on or off OTA updates. -_Since 0.4.7_ +TODO: document system events when an update is received but not yet applied -Determine the version of system firmware available. Returns a version string -of the format: +### System.enableUpdates() -> MAJOR.MINOR.PATCH +Enables OTA updates. Updates are enabled by default. -Such as "0.4.7". +### System.disableUpdates() -For example +Disables OTA updates. An attempt to start an OTA update will fail. + +### System.updatesEnabled() + +Determine if OTA updates are presently enabled or disabled. + +### System.updatesPending() + +Indicates if there are OTA updates pending. + + + + +## String Class + +The String class allows you to use and manipulate strings of text in more complex ways than character arrays do. You can concatenate Strings, append to them, search for and replace substrings, and more. It takes more memory than a simple character array, but it is also more useful. + +For reference, character arrays are referred to as strings with a small s, and instances of the String class are referred to as Strings with a capital S. Note that constant strings, specified in "double quotes" are treated as char arrays, not instances of the String class. + +### String() + +Constructs an instance of the String class. There are multiple versions that construct Strings from different data types (i.e. format them as sequences of characters), including: + + * a constant string of characters, in double quotes (i.e. a char array) + * a single constant character, in single quotes + * another instance of the String object + * a constant integer or long integer + * a constant integer or long integer, using a specified base + * an integer or long integer variable + * an integer or long integer variable, using a specified base +```C++ +// SYNTAX +String(val) +String(val, base) ``` -void setup() -{ - Serial.printlnf("System version: %s", System.version().c_str()); - // prints - // System version: 0.4.7 -} +```cpp +// EXAMPLES +String stringOne = "Hello String"; // using a constant String +String stringOne = String('a'); // converting a constant char into a String +String stringTwo = String("This is a string"); // converting a constant string into a String object +String stringOne = String(stringTwo + " with more"); // concatenating two strings +String stringOne = String(13); // using a constant integer +String stringOne = String(analogRead(0), DEC); // using an int and a base +String stringOne = String(45, HEX); // using an int and a base (hexadecimal) +String stringOne = String(255, BIN); // using an int and a base (binary) +String stringOne = String(millis(), DEC); // using a long and a base ``` +Constructing a String from a number results in a string that contains the ASCII representation of that number. The default is base ten, so -### versionNumber() +`String thisString = String(13)` +gives you the String "13". You can use other bases, however. For example, +`String thisString = String(13, HEX)` +gives you the String "D", which is the hexadecimal representation of the decimal value 13. Or if you prefer binary, +`String thisString = String(13, BIN)` +gives you the String "1101", which is the binary representation of 13. -Determines the version of system firmware available. Returns the version encoded -as a number: -> 0xAABBCCDD - - `AA` is the major release - - `BB` is the minor release - - `CC` is the patch number - - `DD` is 0 +Parameters: -Firmware 0.4.7 has a version number 0x00040700 + * val: a variable to format as a String - string, char, byte, int, long, unsigned int, unsigned long + * base (optional) - the base in which to format an integral value +Returns: an instance of the String class -### buttonPushed() -_Since 0.4.6_ -Can be used to determine how long the System button (MODE on Core/Electron, SETUP on Photon) has been pushed. +### charAt() -Returns `uint16_t` as duration button has been held down in milliseconds. +Access a particular character of the String. ```C++ -// EXAMPLE USAGE -void button_handler(system_event_t event, int duration, void* ) -{ - if (!duration) { // just pressed - RGB.control(true); - RGB.color(255, 0, 255); // MAGENTA - } - else { // just released - RGB.control(false); - } -} - -void setup() -{ - System.on(button_status, button_handler); -} - -void loop() -{ - // it would be nice to fire routine events while - // the button is being pushed, rather than rely upon loop - if (System.buttonPushed() > 1000) { - RGB.color(255, 255, 0); // YELLOW - } -} +// SYNTAX +string.charAt(n) ``` +Parameters: + * `string`: a variable of type String + * `n`: the character to access -### System Cycle Counter +Returns: the n'th character of the String -_Since 0.4.6_ -The system cycle counter is incremented for each instruction executed. It functions -in normal code and during interrupts. Since it operates at the clock frequency -of the device, it can be used for accurately measuring small periods of time. +### compareTo() -```cpp - // overview of System tick functions - uint32_t now = System.ticks(); +Compares two Strings, testing whether one comes before or after the other, or whether they're equal. The strings are compared character by character, using the ASCII values of the characters. That means, for example, that 'a' comes before 'b' but after 'A'. Numbers come before letters. - // for converting an the unknown system tick frequency into microseconds - uint32_t scale = System.ticksPerMicrosecond(); - // delay a given number of ticks. - System.ticksDelay(10); +```C++ +// SYNTAX +string.compareTo(string2) ``` -The system ticks are intended for measuring times from less than a microsecond up -to a second. For longer time periods, using [micros()](#micros-) or [millis()](#millis-) would -be more suitable. +Parameters: + * string: a variable of type String + * string2: another variable of type String -#### ticks() +Returns: -Returns the current value of the system tick count. One tick corresponds to -one cpu cycle. + * a negative number: if string comes before string2 + * 0: if string equals string2 + * a positive number: if string comes after string2 -```cpp - // measure a precise time whens something start - uint32_t ticks = System.ticks(); +### concat() + +Combines, or *concatenates* two strings into one string. The second string is appended to the first, and the result is placed in the original string. +```C++ +// SYNTAX +string.concat(string2) ``` -#### ticksPerMicrosecond(); +Parameters: -Retrieves the number of ticks per microsecond for this device. This is useful -when converting between a number of ticks and time in microseconds. + * string, string2: variables of type String -```cpp +Returns: None - uint32_t start = System.ticks(); - startTheFrobnicator(); - uint32_t end = System.ticks(); - uint32_t duration = (end-start)/System.ticksPerMicrosecond(); +### endsWith() - Serial.printlnf("The frobnicator took %d microseconds to start", duration); +Tests whether or not a String ends with the characters of another String. +```C++ +// SYNTAX +string.endsWith(string2) ``` -#### ticksDelay() +Parameters: -Pause execution a given number of ticks. This can be used to implement precise -delays. + * string: a variable of type String + * string2: another variable of type String -```cpp - // delay 10 ticks. How long this is actually depends upon the clock speed of the - // device. - System.ticksDelay(10); +Returns: - // to delay for 3 microseconds on any device: - System.ticksDelay(3*System.ticksPerMicrosecond()); + * true: if string ends with the characters of string2 + * false: otherwise -``` -The system code has been written such that the compiler can compute the number -of ticks to delay -at compile time and inline the function calls, reducing overhead to a minimum. +### equals() +Compares two strings for equality. The comparison is case-sensitive, meaning the String "hello" is not equal to the String "HELLO". +```C++ +// SYNTAX +string.equals(string2) +``` +Parameters: -### freeMemory() + * string, string2: variables of type String -*Since v0.4.4.* +Returns: -Retrieves the amount of free memory in the system in bytes. + * true: if string equals string2 + * false: otherwise -```cpp -uint32_t freemem = System.freeMemory(); -Serial.print("free memory: "); -Serial.println(freemem); -``` +### equalsIgnoreCase() +Compares two strings for equality. The comparison is not case-sensitive, meaning the String("hello") is equal to the String("HELLO"). +```C++ +// SYNTAX +string.equalsIgnoreCase(string2) +``` +Parameters: -### factoryReset() + * string, string2: variables of type String -This will perform a factory reset and do the following: +Returns: -- Restore factory reset firmware from external flash (tinker) -- Erase Wi-Fi profiles -- Enter Listening mode upon completion + * true: if string equals string2 (ignoring case) + * false: otherwise -```cpp -System.factoryReset() -``` +### format() -### dfu() +_Since 0.4.6_ -The device will enter DFU-mode to allow new user firmware to be refreshed. DFU mode is cancelled by -- flashing firmware to the device using dfu-util, specifying the `:leave` option, or -- a system reset +Provides printf-style formatting for strings. -```cpp -System.dfu() -``` +```C++ -To make DFU mode permanent - so that it continues to enter DFU mode even after a reset until -new firmware is flashed, pass `true` to the `dfu()` function. +Particle.publish("startup", String::format("frobnicator started at %s", Time.timeStr().c_str())); -```cpp -System.dfu(true); // persistent DFU mode - will enter DFU after a reset until firmware is flashed. ``` -### deviceID() - -`System.deviceID()` provides an easy way to extract the device ID of your device. It returns a [String object](#string-class) of the device ID, which is used to identify your device. +### getBytes() -```cpp -// EXAMPLE USAGE +Copies the string's characters to the supplied buffer. -void setup() -{ - // Make sure your Serial Terminal app is closed before powering your device - Serial.begin(9600); - // Now open your Serial Terminal, and hit any key to continue! - while(!Serial.available()) Particle.process(); +```C++ +// SYNTAX +string.getBytes(buf, len) +``` +Parameters: - String myID = System.deviceID(); - // Prints out the device ID over Serial - Serial.println(myID); -} + * string: a variable of type String + * buf: the buffer to copy the characters into (byte []) + * len: the size of the buffer (unsigned int) -void loop() {} -``` +Returns: None -### enterSafeMode() +### indexOf() -_Since 0.4.6_ +Locates a character or String within another String. By default, searches from the beginning of the String, but can also start from a given index, allowing for the locating of all instances of the character or String. ```C++ // SYNTAX -System.enterSafeMode(); +string.indexOf(val) +string.indexOf(val, from) ``` +Parameters: -Resets the device and restarts in safe mode. + * string: a variable of type String + * val: the value to search for - char or String + * from: the index to start the search from +Returns: The index of val within the String, or -1 if not found. -### sleep() [ Sleep ] +### lastIndexOf() -`System.sleep()` can be used to dramatically improve the battery life of a Particle-powered project by temporarily deactivating the Wi-Fi module, which is by far the biggest power draw. +Locates a character or String within another String. By default, searches from the end of the String, but can also work backwards from a given index, allowing for the locating of all instances of the character or String. ```C++ // SYNTAX -System.sleep(long seconds); +string.lastIndexOf(val) +string.lastIndexOf(val, from) ``` -```C++ -// EXAMPLE USAGE +Parameters: -// Put the Wi-Fi module in standby (low power) for 5 seconds -System.sleep(5); -// The device LED will breathe white during sleep -``` + * string: a variable of type String + * val: the value to search for - char or String + * from: the index to work backwards from -`System.sleep(long seconds)` does NOT stop the execution of application code (non-blocking call). Application code will continue running while the Wi-Fi module is in standby mode. +Returns: The index of val within the String, or -1 if not found. -`System.sleep(SLEEP_MODE_DEEP, long seconds)` can be used to put the entire device into a *deep sleep* mode. -In this particular mode, the device shuts down the network subsystem and puts the microcontroller in a stand-by mode. -When the device awakens from deep sleep, it will reset and run all user code from the beginning with no values being maintained in memory from before the deep sleep. +### length() -As such, it is recommended that deep sleep be called only after all user code has completed. The Standby mode is used to achieve the lowest power consumption. After entering Standby mode, the SRAM and register contents are lost except for registers in the backup domain. +Returns the length of the String, in characters. (Note that this doesn't include a trailing null character.) ```C++ // SYNTAX -System.sleep(SLEEP_MODE_DEEP, long seconds); +string.length() ``` -```C++ -// EXAMPLE USAGE - -// Put the device into deep sleep for 60 seconds -System.sleep(SLEEP_MODE_DEEP,60); -// The device LED will shut off during deep sleep -``` -The device will automatically *wake up* and reestablish the Wi-Fi connection after the specified number of seconds. +Parameters: -`System.sleep(uint16_t wakeUpPin, uint16_t edgeTriggerMode)` can be used to put the entire device into a *stop* mode with *wakeup on interrupt*. In this particular mode, the device shuts down the network and puts the microcontroller in a stop mode with configurable wakeup pin and edge triggered interrupt. When the specific interrupt arrives, the device awakens from stop mode, it will behave as if the device is reset and run all user code from the beginning with no values being maintained in memory from before the stop mode. + * string: a variable of type String -As such, it is recommended that stop mode be called only after all user code has completed. (Note: The Photon and Electron will not reset before going into stop mode so all the application variables are preserved after waking up from this mode. The voltage regulator is put in low-power mode. This mode achieves the lowest power consumption while retaining the contents of SRAM and registers.) +Returns: The length of the String in characters. -{{#if core}} -It is mandatory to update the *bootloader* (https://github.com/spark/firmware/tree/bootloader-patch-update) for proper functioning of this mode. -{{/if}} +### remove() -{{#if electron}} -The Electron maintains the cellular connection for the duration of the sleep when `SLEEP_NETWORK_STANDBY` is given as the last parameter value. On wakeup, the device is able to reconnect to the cloud much quicker, at the expense of increased power consumption. -{{/if}} +The String `remove()` function modifies a string, in place, removing chars from the provided index to the end of the string or from the provided index to index plus count. ```C++ // SYNTAX -System.sleep(uint16_t wakeUpPin, uint16_t edgeTriggerMode); -{{#if electron}} -System.sleep(uint16_t wakeUpPin, uint16_t edgeTriggerMode, SLEEP_NETWORK_STANDBY); -{{/if}} +string.remove(index) +string.remove(index,count) ``` -```C++ -// EXAMPLE USAGE +Parameters: -// Put the device into stop mode with wakeup using RISING edge interrupt on D0 pin -System.sleep(D0,RISING); -// The device LED will shut off during sleep -``` + * string: the string which will be modified - a variable of type String + * index: a variable of type unsigned int + * count: a variable of type unsigned int -*Parameters:* +Returns: None -- `wakeUpPin`: the wakeup pin number. supports external interrupts on the following pins: - - D0, D1, D2, D3, D4, A0, A1, A3, A4, A5, A6, A7 -- `edgeTriggerMode`: defines when the interrupt should be triggered. Four constants are predefined as valid values: - - CHANGE to trigger the interrupt whenever the pin changes value, - - RISING to trigger when the pin goes from low to high, - - FALLING for when the pin goes from high to low. +### replace() -`System.sleep(uint16_t wakeUpPin, uint16_t edgeTriggerMode, long seconds)` can be used to put the entire device into a *stop* mode with *wakeup on interrupt* or *wakeup after specified seconds*. In this particular mode, the device shuts network subsystem and puts the microcontroller in a stop mode with configurable wakeup pin and edge triggered interrupt or wakeup after the specified seconds. When the specific interrupt arrives or upon reaching the configured timeout, the device awakens from stop mode. {{#if core}} On the Core, the Core is reset on entering stop mode and runs all user code from the beginning with no values being maintained in memory from before the stop mode. As such, it is recommended that stop mode be called only after all user code has completed.{{/if}} {{#unless core}}The device will not reset before going into stop mode so all the application variables are preserved after waking up from this mode. The voltage regulator is put in low-power mode. This mode achieves the lowest power consumption while retaining the contents of SRAM and registers.{{/unless}} +The String `replace()` function allows you to replace all instances of a given character with another character. You can also use replace to replace substrings of a string with a different substring. ```C++ // SYNTAX -System.sleep(uint16_t wakeUpPin, uint16_t edgeTriggerMode, long seconds{{#if electron}},[SLEEP_NETWORK_STANDBY]{{/if}}); -``` - -```C++ -// EXAMPLE USAGE - -// Put the device into stop mode with wakeup using RISING edge interrupt on D0 pin or wakeup after 60 seconds whichever comes first -System.sleep(D0,RISING,60); -// The device LED will shut off during sleep +string.replace(substring1, substring2) ``` -{{#if core}}On the Core, it is necessary to update the *bootloader* (https://github.com/spark/firmware/tree/bootloader-patch-update) for proper functioning of this mode.{{/if}} +Parameters: + * string: the string which will be modified - a variable of type String + * substring1: searched for - another variable of type String (single or multi-character), char or const char (single character only) + * substring2: replaced with - another variable of type String (signle or multi-character), char or const char (single character only) -*Parameters:* +Returns: None -- `wakeUpPin`: the wakeup pin number. supports external interrupts on the following pins: - - D0, D1, D2, D3, D4, A0, A1, A3, A4, A5, A6, A7 -- `edgeTriggerMode`: defines when the interrupt should be triggered. Four constants are predefined as valid values: - - CHANGE to trigger the interrupt whenever the pin changes value, - - RISING to trigger when the pin goes from low to high, - - FALLING for when the pin goes from high to low. -- `seconds`: wakeup after the specified number of seconds -{{#if electron}} -- `SLEEP_NETWORK_STANDBY`: optional - keeps the cellular modem in a standby state while the device is sleeping.. -{{/if}} +### reserve() -*Power consumption:* +The String reserve() function allows you to allocate a buffer in memory for manipulating strings. -- Core - - In *standard sleep mode*, the device current consumption is in the range of: **30mA to 38mA** - - In *deep sleep mode*, the current consumption is around: **3.2 μA** -- Photon - - Please see the [Photon datasheet](/datasheets/photon-datasheet/#recommended-operating-conditions) +```C++ +// SYNTAX +string.reserve(size) +``` +Parameters: + * size: unsigned int declaring the number of bytes in memory to save for string manipulation -_Since 0.4.5._ The state of the {{#unless electron}}Wi-Fi{{/unless}}{{#if electron}}Cellular{{/if}} and Cloud connections is restored when the system wakes up from sleep. So if the device was connected to the cloud before sleeping, then the cloud connection -is automatically resumed on waking up. -_Since 0.5.0._ In automatic modes, the `sleep()` function doesn't return until the cloud connection has been established. This means that application code can use the cloud connection as soon as `sleep()` returns. In previous versions, it was necessary to call `Particle.process()` to have the cloud reconnected by the system in the background. +Returns: None +```cpp +//EXAMPLE -### reset() +String myString; -Resets the device, just like hitting the reset button or powering down and back up. +void setup() { + // initialize serial and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for Leonardo only + } -```C++ -uint32_t lastReset = 0; + myString.reserve(26); + myString = "i="; + myString += "1234"; + myString += ", is that ok?"; -void setup() { - lastReset = millis(); + // print the String: + Serial.println(myString); } void loop() { - // Reset after 5 minutes of operation - // ================================== - if (millis() - lastReset > 5*60000UL) { - System.reset(); - } + // nothing to do here } ``` +### setCharAt() -## OTA Updates +Sets a character of the String. Has no effect on indices outside the existing length of the String. -Application firmware can use these functions to turn on or off OTA updates. +```C++ +// SYNTAX +string.setCharAt(index, c) +``` +Parameters: -TODO: document system events when an update is received but not yet applied + * string: a variable of type String + * index: the index to set the character at + * c: the character to store to the given location -### System.enableUpdates() +Returns: None -Enables OTA updates. Updates are enabled by default. +### startsWith() -### System.disableUpdates() +Tests whether or not a String starts with the characters of another String. -Disables OTA updates. An attempt to start an OTA update will fail. +```C++ +// SYNTAX +string.startsWith(string2) +``` -### System.updatesEnabled() +Parameters: -Determine if OTA updates are presently enabled or disabled. + * string, string2: variable2 of type String -### System.updatesPending() +Returns: -Indicates if there are OTA updates pending. + * true: if string starts with the characters of string2 + * false: otherwise +### substring() +Get a substring of a String. The starting index is inclusive (the corresponding character is included in the substring), but the optional ending index is exclusive (the corresponding character is not included in the substring). If the ending index is omitted, the substring continues to the end of the String. -## String Class +```C++ +// SYNTAX +string.substring(from) +string.substring(from, to) +``` -The String class allows you to use and manipulate strings of text in more complex ways than character arrays do. You can concatenate Strings, append to them, search for and replace substrings, and more. It takes more memory than a simple character array, but it is also more useful. +Parameters: -For reference, character arrays are referred to as strings with a small s, and instances of the String class are referred to as Strings with a capital S. Note that constant strings, specified in "double quotes" are treated as char arrays, not instances of the String class. + * string: a variable of type String + * from: the index to start the substring at + * to (optional): the index to end the substring before -### String() +Returns: the substring -Constructs an instance of the String class. There are multiple versions that construct Strings from different data types (i.e. format them as sequences of characters), including: +### toCharArray() - * a constant string of characters, in double quotes (i.e. a char array) - * a single constant character, in single quotes - * another instance of the String object - * a constant integer or long integer - * a constant integer or long integer, using a specified base - * an integer or long integer variable - * an integer or long integer variable, using a specified base +Copies the string's characters to the supplied buffer. ```C++ // SYNTAX -String(val) -String(val, base) +string.toCharArray(buf, len) ``` +Parameters: -```cpp -// EXAMPLES + * string: a variable of type String + * buf: the buffer to copy the characters into (char []) + * len: the size of the buffer (unsigned int) -String stringOne = "Hello String"; // using a constant String -String stringOne = String('a'); // converting a constant char into a String -String stringTwo = String("This is a string"); // converting a constant string into a String object -String stringOne = String(stringTwo + " with more"); // concatenating two strings -String stringOne = String(13); // using a constant integer -String stringOne = String(analogRead(0), DEC); // using an int and a base -String stringOne = String(45, HEX); // using an int and a base (hexadecimal) -String stringOne = String(255, BIN); // using an int and a base (binary) -String stringOne = String(millis(), DEC); // using a long and a base -``` -Constructing a String from a number results in a string that contains the ASCII representation of that number. The default is base ten, so +Returns: None -`String thisString = String(13)` -gives you the String "13". You can use other bases, however. For example, -`String thisString = String(13, HEX)` -gives you the String "D", which is the hexadecimal representation of the decimal value 13. Or if you prefer binary, -`String thisString = String(13, BIN)` -gives you the String "1101", which is the binary representation of 13. +### toFloat() +Converts a valid String to a float. The input string should start with a digit. If the string contains non-digit characters, the function will stop performing the conversion. For example, the strings "123.45", "123", and "123fish" are converted to 123.45, 123.00, and 123.00 respectively. Note that "123.456" is approximated with 123.46. Note too that floats have only 6-7 decimal digits of precision and that longer strings might be truncated. +```C++ +// SYNTAX +string.toFloat() +``` Parameters: - * val: a variable to format as a String - string, char, byte, int, long, unsigned int, unsigned long - * base (optional) - the base in which to format an integral value - -Returns: an instance of the String class - + * string: a variable of type String +Returns: float (If no valid conversion could be performed because the string doesn't start with a digit, a zero is returned.) -### charAt() +### toInt() -Access a particular character of the String. +Converts a valid String to an integer. The input string should start with an integral number. If the string contains non-integral numbers, the function will stop performing the conversion. ```C++ // SYNTAX -string.charAt(n) +string.toInt() ``` -Parameters: - - * `string`: a variable of type String - * `n`: the character to access -Returns: the n'th character of the String +Parameters: + * string: a variable of type String -### compareTo() +Returns: long (If no valid conversion could be performed because the string doesn't start with a integral number, a zero is returned.) -Compares two Strings, testing whether one comes before or after the other, or whether they're equal. The strings are compared character by character, using the ASCII values of the characters. That means, for example, that 'a' comes before 'b' but after 'A'. Numbers come before letters. +### toLowerCase() +Get a lower-case version of a String. `toLowerCase()` modifies the string in place. ```C++ // SYNTAX -string.compareTo(string2) +string.toLowerCase() ``` Parameters: * string: a variable of type String - * string2: another variable of type String -Returns: - - * a negative number: if string comes before string2 - * 0: if string equals string2 - * a positive number: if string comes after string2 +Returns: None -### concat() +### toUpperCase() -Combines, or *concatenates* two strings into one string. The second string is appended to the first, and the result is placed in the original string. +Get an upper-case version of a String. `toUpperCase()` modifies the string in place. ```C++ // SYNTAX -string.concat(string2) +string.toUpperCase() ``` Parameters: - * string, string2: variables of type String + * string: a variable of type String Returns: None -### endsWith() +### trim() -Tests whether or not a String ends with the characters of another String. +Get a version of the String with any leading and trailing whitespace removed. ```C++ // SYNTAX -string.endsWith(string2) +string.trim() ``` Parameters: * string: a variable of type String - * string2: another variable of type String -Returns: +Returns: None - * true: if string ends with the characters of string2 - * false: otherwise +## Stream Class +Stream is the base class for character and binary based streams. It is not called directly, but invoked whenever you use a function that relies on it. The Particle Stream Class is based on the Arduino Stream Class. -### equals() +Stream defines the reading functions in Particle. When using any core functionality that uses a read() or similar method, you can safely assume it calls on the Stream class. For functions like print(), Stream inherits from the Print class. + +Some of the Particle classes that rely on Stream include : +`Serial` +`Wire` +`TCPClient` +`UDP` -Compares two strings for equality. The comparison is case-sensitive, meaning the String "hello" is not equal to the String "HELLO". +### setTimeout() +`setTimeout()` sets the maximum milliseconds to wait for stream data, it defaults to 1000 milliseconds. ```C++ // SYNTAX -string.equals(string2) +stream.setTimeout(time); ``` -Parameters: - - * string, string2: variables of type String -Returns: +Parameters: - * true: if string equals string2 - * false: otherwise + * stream: an instance of a class that inherits from Stream + * time: timeout duration in milliseconds (unsigned int) -### equalsIgnoreCase() +Returns: None -Compares two strings for equality. The comparison is not case-sensitive, meaning the String("hello") is equal to the String("HELLO"). +### find() +`find()` reads data from the stream until the target string of given length is found. ```C++ // SYNTAX -string.equalsIgnoreCase(string2) +stream.find(target); // reads data from the stream until the target string is found +stream.find(target, length); // reads data from the stream until the target string of given length is found ``` -Parameters: - - * string, string2: variables of type String -Returns: - - * true: if string equals string2 (ignoring case) - * false: otherwise +Parameters: -### format() + * stream : an instance of a class that inherits from Stream + * target : pointer to the string to search for (char *) + * length : length of target string to search for (size_t) -*Since 0.4.6.* +Returns: returns true if target string is found, false if timed out -Provides printf-style formatting for strings. +### findUntil() +`findUntil()` reads data from the stream until the target string or terminator string is found. ```C++ - -Particle.publish("startup", String::format("frobnicator started at %s", Time.timeStr().c_str())); - +// SYNTAX +stream.findUntil(target, terminal); // reads data from the stream until the target string or terminator is found +stream.findUntil(target, terminal, length); // reads data from the stream until the target string of given length or terminator is found ``` +Parameters: + + * stream : an instance of a class that inherits from Stream + * target : pointer to the string to search (char *) + * terminal : pointer to the terminal string to search for (char *) + * length : length of target string to search for (size_t) -### getBytes() +Returns: returns true if target string or terminator string is found, false if timed out -Copies the string's characters to the supplied buffer. +### readBytes() +`readBytes()` read characters from a stream into a buffer. The function terminates if the determined length has been read, or it times out. ```C++ // SYNTAX -string.getBytes(buf, len) +stream.readBytes(buffer, length); ``` -Parameters: - * string: a variable of type String - * buf: the buffer to copy the characters into (byte []) - * len: the size of the buffer (unsigned int) +Parameters: -Returns: None + * stream : an instance of a class that inherits from Stream + * buffer : pointer to the buffer to store the bytes in (char *) + * length : the number of bytes to read (size_t) -### indexOf() +Returns: returns the number of characters placed in the buffer (0 means no valid data found) -Locates a character or String within another String. By default, searches from the beginning of the String, but can also start from a given index, allowing for the locating of all instances of the character or String. +### readBytesUntil() +`readBytesUntil()` reads characters from a stream into a buffer. The function terminates if the terminator character is detected, the determined length has been read, or it times out. ```C++ // SYNTAX -string.indexOf(val) -string.indexOf(val, from) +stream.readBytesUntil(terminator, buffer, length); ``` Parameters: - * string: a variable of type String - * val: the value to search for - char or String - * from: the index to start the search from - -Returns: The index of val within the String, or -1 if not found. + * stream : an instance of a class that inherits from Stream + * terminator : the character to search for (char) + * buffer : pointer to the buffer to store the bytes in (char *) + * length : the number of bytes to read (size_t) -### lastIndexOf() +Returns: returns the number of characters placed in the buffer (0 means no valid data found) -Locates a character or String within another String. By default, searches from the end of the String, but can also work backwards from a given index, allowing for the locating of all instances of the character or String. +### readString() +`readString()` reads characters from a stream into a string. The function terminates if it times out. ```C++ // SYNTAX -string.lastIndexOf(val) -string.lastIndexOf(val, from) +stream.readString(); ``` Parameters: - * string: a variable of type String - * val: the value to search for - char or String - * from: the index to work backwards from - -Returns: The index of val within the String, or -1 if not found. + * stream : an instance of a class that inherits from Stream -### length() +Returns: the entire string read from stream (String) -Returns the length of the String, in characters. (Note that this doesn't include a trailing null character.) +### readStringUntil() +`readStringUntil()` reads characters from a stream into a string until a terminator character is detected. The function terminates if it times out. ```C++ // SYNTAX -string.length() +stream.readStringUntil(terminator); ``` Parameters: - * string: a variable of type String + * stream : an instance of a class that inherits from Stream + * terminator : the character to search for (char) -Returns: The length of the String in characters. +Returns: the entire string read from stream, until the terminator character is detected -### remove() +### parseInt() +`parseInt()` returns the first valid (long) integer value from the current position under the following conditions: -The String `remove()` function modifies a string, in place, removing chars from the provided index to the end of the string or from the provided index to index plus count. + - Initial characters that are not digits or a minus sign, are skipped; + - Parsing stops when no characters have been read for a configurable time-out value, or a non-digit is read; ```C++ // SYNTAX -string.remove(index) -string.remove(index,count) +stream.parseInt(); +stream.parseInt(skipChar); // allows format characters (typically commas) in values to be ignored ``` Parameters: - * string: the string which will be modified - a variable of type String - * index: a variable of type unsigned int - * count: a variable of type unsigned int - -Returns: None + * stream : an instance of a class that inherits from Stream + * skipChar : the character to ignore while parsing (char). -### replace() +Returns: parsed int value (long). If no valid digits were read when the time-out occurs, 0 is returned. -The String `replace()` function allows you to replace all instances of a given character with another character. You can also use replace to replace substrings of a string with a different substring. +### parseFloat() +`parseFloat()` as `parseInt()` but returns the first valid floating point value from the current position. ```C++ // SYNTAX -string.replace(substring1, substring2) +stream.parsetFloat(); +stream.parsetFloat(skipChar); // allows format characters (typically commas) in values to be ignored ``` Parameters: - * string: the string which will be modified - a variable of type String - * substring1: searched for - another variable of type String (single or multi-character), char or const char (single character only) - * substring2: replaced with - another variable of type String (signle or multi-character), char or const char (single character only) + * stream : an instance of a class that inherits from Stream + * skipChar : the character to ignore while parsing (char). -Returns: None +Returns: parsed float value (float). If no valid digits were read when the time-out occurs, 0 is returned. -### reserve() -The String reserve() function allows you to allocate a buffer in memory for manipulating strings. -```C++ -// SYNTAX -string.reserve(size) -``` -Parameters: +## Logging - * size: unsigned int declaring the number of bytes in memory to save for string manipulation +_Since 0.6.0_ -Returns: None +This library provides various classes for logging. ```cpp -//EXAMPLE +// EXAMPLE -String myString; +// Use primary serial over USB interface for logging output +SerialLogHandler logHandler; void setup() { - // initialize serial and wait for port to open: - Serial.begin(9600); - while (!Serial) { - ; // wait for serial port to connect. Needed for Leonardo only - } - - myString.reserve(26); - myString = "i="; - myString += "1234"; - myString += ", is that ok?"; + // Log some messages with different logging levels + Log.info("This is info message"); + Log.warn("This is warning message"); + Log.error("This is error message"); - // print the String: - Serial.println(myString); + // Format text message + Log.info("System version: %s", (const char*)System.version()); } void loop() { - // nothing to do here } ``` -### setCharAt() - -Sets a character of the String. Has no effect on indices outside the existing length of the String. - -```C++ -// SYNTAX -string.setCharAt(index, c) -``` -Parameters: - - * string: a variable of type String - * index: the index to set the character at - * c: the character to store to the given location - -Returns: None +At higher level, the logging framework consists of two parts represented by their respective classes: [loggers](#logger-class) and [log handlers](#log-handlers). Most of the logging operations, such as generating a log message, are done through logger instances, while log handlers act as _sinks_ for the overall logging output generated by the system and application modules. -### startsWith() +The library provides default logger instance named `Log`, which can be used for all typical logging operations. Note that applications still need to instantiate at least one log handler in order to enable logging, otherwise most of the logging operations will have no effect. In the provided example, the application uses `SerialLogHandler` which sends the logging output to the primary serial over USB interface. -Tests whether or not a String starts with the characters of another String. +Consider the following logging output as generated by the example application: -```C++ -// SYNTAX -string.startsWith(string2) -``` +`0000000047 [app] INFO: This is info message` +`0000000050 [app] WARN: This is warning message` +`0000000100 [app] ERROR: This is error message` +`0000000149 [app] INFO: System version: 0.6.0` -Parameters: +Here, each line starts with a timestamp (a number of milliseconds since the system startup), `app` is a default [logging category](#logging-categories), and `INFO`, `WARN` and `ERROR` are [logging levels](#logging-levels) of the respective log messages. - * string, string2: variable2 of type String +### Logging Levels -Returns: +Every log message is always associated with some logging level that describes _severity_ of the message. Supported logging levels are defined by the `LogLevel` enum (from lowest to highest level): - * true: if string starts with the characters of string2 - * false: otherwise + * `LOG_LEVEL_ALL` : special value that can be used to enable logging of all messages + * `LOG_LEVEL_TRACE` : verbose output for debugging purposes + * `LOG_LEVEL_INFO` : regular information messages + * `LOG_LEVEL_WARN` : warnings and non-critical errors + * `LOG_LEVEL_ERROR` : error messages + * `LOG_LEVEL_NONE` : special value that can be used to disable logging of any messages +```cpp +// EXAMPLE - message logging -### substring() +Log.trace("This is trace message"); +Log.info("This is info message"); +Log.warn("This is warning message"); +Log.error("This is error message"); -Get a substring of a String. The starting index is inclusive (the corresponding character is included in the substring), but the optional ending index is exclusive (the corresponding character is not included in the substring). If the ending index is omitted, the substring continues to the end of the String. +// Specify logging level directly +Log(LOG_LEVEL_INFO, "This is info message"); -```C++ -// SYNTAX -string.substring(from) -string.substring(from, to) +// Log message with the default logging level (LOG_LEVEL_INFO) +Log("This is info message"); ``` -Parameters: +For convenience, [Logger class](#logger-class) (and its default `Log` instance) provides separate logging method for each of the defined logging levels. - * string: a variable of type String - * from: the index to start the substring at - * to (optional): the index to end the substring before +Log handlers can be configured to filter out messages that are below a certain logging level. By default, any messages below the `LOG_LEVEL_INFO` level are filtered out. -Returns: the substring +```cpp +// EXAMPLE - basic filtering -### toCharArray() +// Log handler processing only warning and error messages +SerialLogHandler logHandler(LOG_LEVEL_WARN); -Copies the string's characters to the supplied buffer. +void setup() { + Log.trace("This is trace message"); // Ignored by the handler + Log.info("This is info message"); // Ignored by the handler + Log.warn("This is warning message"); + Log.error("This is error message"); +} -```C++ -// SYNTAX -string.toCharArray(buf, len) +void loop() { +} ``` -Parameters: - - * string: a variable of type String - * buf: the buffer to copy the characters into (char []) - * len: the size of the buffer (unsigned int) -Returns: None +In the provided example, the trace and info messages will be filtered out according to the log handler settings, which prevent log messages below the `LOG_LEVEL_WARN` level from being logged: -### toFloat() +`0000000050 [app] WARN: This is warning message` +`0000000100 [app] ERROR: This is error message` -Converts a valid String to a float. The input string should start with a digit. If the string contains non-digit characters, the function will stop performing the conversion. For example, the strings "123.45", "123", and "123fish" are converted to 123.45, 123.00, and 123.00 respectively. Note that "123.456" is approximated with 123.46. Note too that floats have only 6-7 decimal digits of precision and that longer strings might be truncated. +### Logging Categories -```C++ -// SYNTAX -string.toFloat() -``` +In addition to logging level, log messages can also be associated with some _category_ name. Categories allow to organize system and application modules into namespaces, and are used for more selective filtering of the logging output. -Parameters: +One of the typical use cases for category filtering is suppressing of non-critical system messages while preserving application messages at lower logging levels. In the provided example, a message that is not associated with the `app` category will be logged only if its logging level is at or above the warning level (`LOG_LEVEL_WARN`). - * string: a variable of type String +```cpp +// EXAMPLE - filtering out system messages -Returns: float (If no valid conversion could be performed because the string doesn't start with a digit, a zero is returned.) +SerialLogHandler logHandler(LOG_LEVEL_WARN, { // Logging level for non-application messages + { "app", LOG_LEVEL_ALL } // Logging level for application messages +}); +``` -### toInt() +Default `Log` logger uses `app` category for all messages generated via its logging methods. In order to log messages with different category name it is necessary to instantiate another logger, passing category name to its constructor. -Converts a valid String to an integer. The input string should start with an integral number. If the string contains non-integral numbers, the function will stop performing the conversion. +```cpp +// EXAMPLE - using custom loggers -```C++ -// SYNTAX -string.toInt() -``` +void connect() { + Logger log("app.network"); + log.trace("Connecting to server"); // Using local logger +} -Parameters: +SerialLogHandler logHandler(LOG_LEVEL_WARN, { // Logging level for non-application messages + { "app", LOG_LEVEL_INFO }, // Default logging level for all application messages + { "app.network", LOG_LEVEL_TRACE } // Logging level for networking messages +}); - * string: a variable of type String +void setup() { + Log.info("System started"); // Using default logger instance + Log.trace("My device ID: %s", (const char*)System.deviceID()); + connect(); +} -Returns: long (If no valid conversion could be performed because the string doesn't start with a integral number, a zero is returned.) +void loop() { +} +``` -### toLowerCase() +Category names are written in all lower case and may contain arbitrary number of _subcategories_ separated by period character. In order to not interfere with the system logging, it is recommended to always add `app` prefix to all application-specific category names. -Get a lower-case version of a String. `toLowerCase()` modifies the string in place. +The example application generates the following logging output: -```C++ -// SYNTAX -string.toLowerCase() -``` +`0000000044 [app] INFO: System started` +`0000000044 [app.network] TRACE: Connecting to server` -Parameters: +Note that the trace message containing device ID has been filtered out according to the log handler settings, which prevent log messages with the `app` category from being logged if their logging level is below the `LOG_LEVEL_INFO` level. - * string: a variable of type String +Category filters are specified using _initializer list_ syntax with each element of the list containing a filter string and a minimum logging level required for messages with matching category to be logged. Note that filter string matches not only exact category name but any of its subcategory names as well, for example: -Returns: None + * `a` – matches `a`, `a.b`, `a.b.c` but not `aaa` or `aaa.b` + * `b.c` – matches `b.c`, `b.c.d` but not `a.b.c` or `b.ccc` -### toUpperCase() +If more than one filter matches a given category name, the most specific filter is used. -Get an upper-case version of a String. `toUpperCase()` modifies the string in place. +### Additional Attributes -```C++ -// SYNTAX -string.toUpperCase() -``` +As described in previous sections, certain log message attributes, such as a timestamp, are automatically added to all generated messages. The library also defines some attributes that can be used for application-specific needs: -Parameters: + * `code` : arbitrary integer value (e.g. error code) + * `details` : description string (e.g. error message) - * string: a variable of type String +```cpp +// EXAMPLE - specifying additional attributes -Returns: None +SerialLogHandler logHandler; -### trim() +int connect() { + return ECONNREFUSED; // Return an error +} -Get a version of the String with any leading and trailing whitespace removed. +void setup() { + Log.info("Connecting to server"); + int error = connect(); + if (error) { + // Get error message string + const char *message = strerror(error); + // Log message with additional attributes + Log.code(error).details(message).error("Connection error"); + } +} -```C++ -// SYNTAX -string.trim() +void loop() { +} ``` -Parameters: - - * string: a variable of type String +The example application specifies `code` and `details` attributes for the error message, generating the following logging output: -Returns: None +`0000000084 [app] INFO: Connecting to server` +`0000000087 [app] ERROR: Connection error [code = 111, details = Connection refused]` +### Log Handlers -## Stream Class -Stream is the base class for character and binary based streams. It is not called directly, but invoked whenever you use a function that relies on it. The Particle Stream Class is based on the Arduino Stream Class. +In order to enable logging, application needs to instantiate at least one log handler. If necessary, several different log handlers can be instantiated at the same time. -Stream defines the reading functions in Particle. When using any core functionality that uses a read() or similar method, you can safely assume it calls on the Stream class. For functions like print(), Stream inherits from the Print class. +```cpp +// EXAMPLE - enabling multiple log handlers -Some of the Particle classes that rely on Stream include : -`Serial` -`Wire` -`TCPClient` -`UDP` +SerialLogHandler logHandler1; +Serial1LogHandler logHandler2(57600); // Baud rate -### setTimeout() -`setTimeout()` sets the maximum milliseconds to wait for stream data, it defaults to 1000 milliseconds. +void setup() { + Log.info("This is info message"); // Processed by all handlers +} -```C++ -// SYNTAX -stream.setTimeout(time); +void loop() { +} ``` -Parameters: - - * stream: an instance of a class that inherits from Stream - * time: timeout duration in milliseconds (unsigned int) +The library provides the following log handlers: -Returns: None +`SerialLogHandler` -### find() -`find()` reads data from the stream until the target string of given length is found. +This handler uses primary serial over USB interface for the logging output ([Serial](#serial)). -```C++ -// SYNTAX -stream.find(target); // reads data from the stream until the target string is found -stream.find(target, length); // reads data from the stream until the target string of given length is found -``` +`SerialLogHandler(LogLevel level, const Filters &filters)` Parameters: - * stream : an instance of a class that inherits from Stream - * target : pointer to the string to search for (char *) - * length : length of target string to search for (size_t) + * level : default logging level (default value is `LOG_LEVEL_INFO`) + * filters : category filters (not specified by default) -Returns: returns true if target string is found, false if timed out +`Serial1LogHandler` -### findUntil() -`findUntil()` reads data from the stream until the target string or terminator string is found. +This handler uses the device's TX and RX pins for the logging output ([Serial1](#serial)). -```C++ -// SYNTAX -stream.findUntil(target, terminal); // reads data from the stream until the target string or terminator is found -stream.findUntil(target, terminal, length); // reads data from the stream until the target string of given length or terminator is found -``` +`Serial1LogHandler(LogLevel level, const Filters &filters)` +`Serial1LogHandler(int baud, LogLevel level, const Filters &filters)` Parameters: - * stream : an instance of a class that inherits from Stream - * target : pointer to the string to search (char *) - * terminal : pointer to the terminal string to search for (char *) - * length : length of target string to search for (size_t) + * level : default logging level (default value is `LOG_LEVEL_INFO`) + * filters : category filters (not specified by default) + * baud : baud rate (default value is 9600) -Returns: returns true if target string or terminator string is found, false if timed out +### Logger Class -### readBytes() -`readBytes()` read characters from a stream into a buffer. The function terminates if the determined length has been read, or it times out. +This class is used to generate log messages. The library also provides default instance of this class named `Log`, which can be used for all typical logging operations. -```C++ -// SYNTAX -stream.readBytes(buffer, length); +`Logger()` +`Logger(const char *name)` + +```cpp +// EXAMPLE +Logger myLogger("app.main"); ``` -Parameters: +Construct logger. - * stream : an instance of a class that inherits from Stream - * buffer : pointer to the buffer to store the bytes in (char *) - * length : the number of bytes to read (size_t) +Parameters: -Returns: returns the number of characters placed in the buffer (0 means no valid data found) + * name : category name (default value is `app`) -### readBytesUntil() -`readBytesUntil()` reads characters from a stream into a buffer. The function terminates if the terminator character is detected, the determined length has been read, or it times out. +`const char* name()` -```C++ -// SYNTAX -stream.readBytesUntil(terminator, buffer, length); +```cpp +// EXAMPLE +const char *name = Log.name(); // Returns "app" ``` -Parameters: - - * stream : an instance of a class that inherits from Stream - * terminator : the character to search for (char) - * buffer : pointer to the buffer to store the bytes in (char *) - * length : the number of bytes to read (size_t) +Returns category name set for this logger. -Returns: returns the number of characters placed in the buffer (0 means no valid data found) +`void trace(const char *format, ...)` +`void info(const char *format, ...)` +`void warn(const char *format, ...)` +`void error(const char *format, ...)` -### readString() -`readString()` reads characters from a stream into a string. The function terminates if it times out. +```cpp +// EXAMPLE +Log.trace("This is trace message"); +Log.info("This is info message"); +Log.warn("This is warn message"); +Log.error("This is error message"); -```C++ -// SYNTAX -stream.readString(); +// Format text message +Log.info("The secret of everything is %d", 42); ``` -Parameters: +Generate trace, info, warning or error message respectively. - * stream : an instance of a class that inherits from Stream +Parameters: -Returns: the entire string read from stream (String) + * format : format string -### readStringUntil() -`readStringUntil()` reads characters from a stream into a string until a terminator character is detected. The function terminates if it times out. +`void log(const char *format, ...)` +`void operator()(const char *format, ...)` -```C++ -// SYNTAX -stream.readStringUntil(terminator); +```cpp +// EXAMPLE +Log("The secret of everything is %d", 42); // Generates info message ``` -Parameters: - - * stream : an instance of a class that inherits from Stream - * terminator : the character to search for (char) +Generates log message with the default logging level (`LOG_LEVEL_INFO`). -Returns: the entire string read from stream, until the terminator character is detected +Parameters: -### parseInt() -`parseInt()` returns the first valid (long) integer value from the current position under the following conditions: + * format : format string - - Initial characters that are not digits or a minus sign, are skipped; - - Parsing stops when no characters have been read for a configurable time-out value, or a non-digit is read; +`void log(LogLevel level, const char *format, ...)` +`void operator()(LogLevel level, const char *format, ...)` -```C++ -// SYNTAX -stream.parseInt(); -stream.parseInt(skipChar); // allows format characters (typically commas) in values to be ignored +```cpp +// EXAMPLE +Log(LOG_LEVEL_INFO, "The secret of everything is %d", 42); ``` +Generates log message with the specified logging level. + Parameters: - * stream : an instance of a class that inherits from Stream - * skipChar : the character to ignore while parsing (char). + * format : format string + * level : logging level (default value is `LOG_LEVEL_INFO`) -Returns: parsed int value (long). If no valid digits were read when the time-out occurs, 0 is returned. +`bool isTraceEnabled()` +`bool isInfoEnabled()` +`bool isWarnEnabled()` +`bool isErrorEnabled()` -### parseFloat() -`parseFloat()` as `parseInt()` but returns the first valid floating point value from the current position. +```cpp +// EXAMPLE +if (Log.isTraceEnabled()) { + // Do some heavy logging +} +``` -```C++ -// SYNTAX -stream.parsetFloat(); -stream.parsetFloat(skipChar); // allows format characters (typically commas) in values to be ignored +Return `true` if logging is enabled for trace, info, warning or error messages respectively. + +`bool isLevelEnabled(LogLevel level)` + +```cpp +// EXAMPLE +if (Log.isLevelEnabled(LOG_LEVEL_TRACE)) { + // Do some heavy logging +} ``` -Parameters: +Returns `true` if logging is enabled for the specified logging level. - * stream : an instance of a class that inherits from Stream - * skipChar : the character to ignore while parsing (char). +Parameters: -Returns: parsed float value (float). If no valid digits were read when the time-out occurs, 0 is returned. + * level : logging level @@ -9542,3 +11571,335 @@ However, there might be instances where the preprocessor causes issues in your c So when you see the `ABC does not name a type` error, yet you know the type is defined, consider disabling the preprocessor using `#pragma SPARK_NO_PREPROCESSOR` at the top of your code. + +## Firmware Releases + +Particle device firmware is open source and stored [here on Github](https://github.com/spark/firmware). + +Firmware releases are published [here on Github](https://github.com/spark/firmware/releases) as they are created, tested and deployed. + +### Firmware Release Process + +The process in place for releasing all firmware prerelease or default release versions can be found [here on Github](https://github.com/spark/firmware/wiki/Firmware-Release-Process). + +### Github Release Notes + +Please go to Github to read the Changelog for your desired firmware version (Click a version below). + +|Firmware Version (Github Release Notes)| +|:-:|:-:|:-:|:-:|:-:| +|v0.6.x default releases|[v0.6.0](https://github.com/spark/firmware/releases/tag/v0.6.0)|||| +|v0.6.x-rc.x prereleases|[v0.6.0-rc.1](https://github.com/spark/firmware/releases/tag/v0.6.0-rc.1)|[v0.6.0-rc.2](https://github.com/spark/firmware/releases/tag/v0.6.0-rc.2)|-|-| +|v0.5.x default releases|[v0.5.0](https://github.com/spark/firmware/releases/tag/v0.5.0)|[v0.5.1](https://github.com/spark/firmware/releases/tag/v0.5.1)|[v0.5.2](https://github.com/spark/firmware/releases/tag/v0.5.2)|[v0.5.3](https://github.com/spark/firmware/releases/tag/v0.5.3)| +|v0.5.x-rc.x prereleases|[v0.5.3-rc.1](https://github.com/spark/firmware/releases/tag/v0.5.3-rc.1)|[v0.5.3-rc.2](https://github.com/spark/firmware/releases/tag/v0.5.3-rc.2)|[v0.5.3-rc.3](https://github.com/spark/firmware/releases/tag/v0.5.3-rc.3)|-| + +### Programming and Debugging Notes + +If you don't see any notes below or if they are the wrong version, please select your Firmware Version below to reload the page with the correct notes. Otherwise, you must have come here from a firmware release page on Github and your version's notes will be found below :) + +|Firmware Version (Programming and Debugging Notes in Docs)| +|:-:|:-:|:-:|:-:|:-:| +|v0.6.x default releases|[v0.6.0](https://docs.particle.io/reference/firmware/photon/?fw_ver=0.6.0&cli_ver=1.18.0&electron_parts=3#programming-and-debugging-notes)|||| +|v0.6.x-rc.x prereleases|[v0.6.0-rc.1](https://prerelease-docs.particle.io/reference/firmware/photon/?fw_ver=0.6.0-rc.1&cli_ver=1.17.0&electron_parts=3#programming-and-debugging-notes)|[v0.6.0-rc.2](https://prerelease-docs.particle.io/reference/firmware/photon/?fw_ver=0.6.0-rc.2&cli_ver=1.17.0&electron_parts=3#programming-and-debugging-notes)|-|-| +|v0.5.x default releases|[v0.5.0](https://docs.particle.io/reference/firmware/photon/?fw_ver=0.5.0&cli_ver=1.12.0&electron_parts=2#programming-and-debugging-notes)|[v0.5.1](https://docs.particle.io/reference/firmware/photon/?fw_ver=0.5.1&cli_ver=1.14.2&electron_parts=2#programming-and-debugging-notes)|[v0.5.2](https://docs.particle.io/reference/firmware/photon/?fw_ver=0.5.2&cli_ver=1.15.0&electron_parts=2#programming-and-debugging-notes)|[v0.5.3](https://docs.particle.io/reference/firmware/photon/?fw_ver=0.5.3&cli_ver=1.17.0&electron_parts=2#programming-and-debugging-notes)| +|v0.5.x-rc.x prereleases|[v0.5.3-rc.1](https://prerelease-docs.particle.io/reference/firmware/photon/?fw_ver=0.5.3-rc.1&cli_ver=1.15.0&electron_parts=2#programming-and-debugging-notes)|[v0.5.3-rc.2](https://prerelease-docs.particle.io/reference/firmware/photon/?fw_ver=0.5.3-rc.2&cli_ver=1.16.0&electron_parts=2#programming-and-debugging-notes)|[v0.5.3-rc.3](https://prerelease-docs.particle.io/reference/firmware/photon/?fw_ver=0.5.3-rc.3&cli_ver=1.16.0&electron_parts=2#programming-and-debugging-notes)|-| + + + +#### release-notes-wrapper + + +##### @FW_VER@0.5.0if +##### @FW_VER@0.5.0endif +##### @FW_VER@0.5.1if +##### @FW_VER@0.5.1endif +##### @FW_VER@0.5.2if +##### @FW_VER@0.5.2endif +##### @FW_VER@0.5.3if +##### @FW_VER@0.5.3endif +##### @FW_VER@0.5.4if +##### @FW_VER@0.5.4endif +##### @FW_VER@0.6.0if +##### @FW_VER@0.6.0endif +##### @CLI_VER@1.15.0if +##### @CLI_VER@1.15.0endif +##### @CLI_VER@1.17.0if +##### @CLI_VER@1.17.0endif +##### @CLI_VER@1.18.0if +##### @CLI_VER@1.18.0endif +##### @ELECTRON_PARTS@2if +##### @ELECTRON_PARTS@2endif +##### @ELECTRON_PARTS@3if +##### @ELECTRON_PARTS@3endif + + + +The following instructions are for upgrading to **System Firmware v@FW_VER@** which requires **Particle CLI v@CLI_VER@**. + +**Updating System Firmware Automatically** + +To update your Photon, P1 or Core system firmware automatically, compile and flash your application in the [Build IDE](https://build.particle.io), selecting version **@FW_VER@** in the devices drawer. The app will be flashed, following by the system part1 and part2 firmware for Photon and P1. Other update instructions for Core, Photon, P1 and Electron can be found below. + +--- + +**The easy local method using Particle CLI** + +The easiest way to upgrade to System Firmware Version @FW_VER@ is to use the Particle CLI with a single command. You will first upgrade the system firmware, then optionally program Tinker on the device. This **requires CLI version @CLI_VER@**. You can check with `particle --version`. + +If you have the [Particle CLI](https://github.com/spark/particle-cli) installed already, you can update it with the following command `sudo npm update -g particle-cli@v@CLI_VER@` (note: you can try without sudo first if you wish). + +To upgrade system firmware, make sure the device is in [DFU mode](http://docs.particle.io/photon/modes/#selecting-various-modes-dfu-mode-device-firmware-upgrade) (flashing yellow LED) and run these commands in order: + +``` +The easy local method using Particle CLI + +1) Make sure the device is in DFU mode and run: + +particle update + +2) Optionally add Tinker as the user firmware instead of an app that you may currently have running on your device. Have the device in DFU mode and run: + +particle flash --usb tinker +``` + +--- + +**The OTA method using Particle CLI** + +##### @FW_VER@0.6.0if +**Note:** You must update your Electron to (v0.5.3, v0.5.3-rc.2, or v0.5.3-rc.3) first before attempting to use OTA or YModem transfer to update to v0.6.0. If you use DFU over USB, you can update to v0.6.0 directly, but make sure you have installed v1.18.0 of the CLI first. +##### @FW_VER@0.6.0endif + +**Note**: You must download system binaries to a local directory on your machine for this to work. Binaries are attached to the bottom of the [Github Release Notes](#github-release-notes). + +If your device is online, you can attempt to OTA (Over The Air) update these system parts as well with the Particle CLI. Run the following commands in order for your device type: + +##### @ELECTRON_PARTS@2if +``` +The OTA method using Particle CLI + +// Core +particle flash YOUR_DEVICE_NAME tinker-@FW_VER@-core.bin + +// Photon +particle flash YOUR_DEVICE_NAME system-part1-@FW_VER@-photon.bin +particle flash YOUR_DEVICE_NAME system-part2-@FW_VER@-photon.bin +particle flash YOUR_DEVICE_NAME tinker (optional) + +// P1 +particle flash YOUR_DEVICE_NAME system-part1-@FW_VER@-p1.bin +particle flash YOUR_DEVICE_NAME system-part2-@FW_VER@-p1.bin +particle flash YOUR_DEVICE_NAME tinker (optional) + +// Electron +particle flash YOUR_DEVICE_NAME system-part1-@FW_VER@-electron.bin +particle flash YOUR_DEVICE_NAME system-part2-@FW_VER@-electron.bin +particle flash YOUR_DEVICE_NAME tinker (optional) +``` +##### @ELECTRON_PARTS@2endif + +##### @ELECTRON_PARTS@3if +``` +The OTA method using Particle CLI + +// Core +particle flash YOUR_DEVICE_NAME tinker-@FW_VER@-core.bin + +// Photon +particle flash YOUR_DEVICE_NAME system-part1-@FW_VER@-photon.bin +particle flash YOUR_DEVICE_NAME system-part2-@FW_VER@-photon.bin +particle flash YOUR_DEVICE_NAME tinker (optional) + +// P1 +particle flash YOUR_DEVICE_NAME system-part1-@FW_VER@-p1.bin +particle flash YOUR_DEVICE_NAME system-part2-@FW_VER@-p1.bin +particle flash YOUR_DEVICE_NAME tinker (optional) + +// Electron +particle flash YOUR_DEVICE_NAME system-part1-@FW_VER@-electron.bin +particle flash YOUR_DEVICE_NAME system-part2-@FW_VER@-electron.bin +particle flash YOUR_DEVICE_NAME system-part3-@FW_VER@-electron.bin +particle flash YOUR_DEVICE_NAME tinker (optional) +``` +##### @ELECTRON_PARTS@3endif + +--- + +**The local method over USB using Particle CLI** + +This **requires CLI version @CLI_VER@ or newer**. You can check with `particle --version`. + +If you have the [Particle CLI](https://github.com/spark/particle-cli) installed already, you can update it with the following command `sudo npm update -g particle-cli` (note: you can try without sudo first if you wish). + +To upgrade system firmware, make sure the device is in [DFU mode](http://docs.particle.io/photon/modes/#selecting-various-modes-dfu-mode-device-firmware-upgrade) (flashing yellow LED) and run these commands in order for your device type: + +##### @ELECTRON_PARTS@2if +``` +The local method over USB using Particle CLI + +// Core +particle flash --usb tinker-@FW_VER@-core.bin + +// Photon +particle flash --usb system-part1-@FW_VER@-photon.bin +particle flash --usb system-part2-@FW_VER@-photon.bin +particle flash --usb tinker (optional) + +// P1 +particle flash --usb system-part1-@FW_VER@-p1.bin +particle flash --usb system-part2-@FW_VER@-p1.bin +particle flash --usb tinker (optional) + +// Electron +particle flash --usb system-part1-@FW_VER@-electron.bin +particle flash --usb system-part2-@FW_VER@-electron.bin +particle flash --usb tinker (optional) +``` +##### @ELECTRON_PARTS@2endif + +##### @ELECTRON_PARTS@3if +``` +The local method over USB using Particle CLI + +// Core +particle flash --usb tinker-@FW_VER@-core.bin + +// Photon +particle flash --usb system-part1-@FW_VER@-photon.bin +particle flash --usb system-part2-@FW_VER@-photon.bin +particle flash --usb tinker (optional) + +// P1 +particle flash --usb system-part1-@FW_VER@-p1.bin +particle flash --usb system-part2-@FW_VER@-p1.bin +particle flash --usb tinker (optional) + +// Electron +particle flash --usb system-part1-@FW_VER@-electron.bin +particle flash --usb system-part2-@FW_VER@-electron.bin +particle flash --usb system-part3-@FW_VER@-electron.bin +particle flash --usb tinker (optional) +``` +##### @ELECTRON_PARTS@3endif + +--- + +**The local DFU-UTIL method** +can be applied to offline devices locally over USB using `dfu-util` +- Put the device in [DFU mode](http://docs.particle.io/photon/modes/#selecting-various-modes-dfu-mode-device-firmware-upgrade) (flashing yellow LED) +- open a terminal window, change to the directory where you downloaded the files above, and run these commands in order for your device type: + +##### @ELECTRON_PARTS@2if +``` +The local DFU-UTIL method + +// Core +dfu-util -d 1d50:607f -a 0 -s 0x8005000:leave -D tinker-@FW_VER@-core.bin + +// Photon +dfu-util -d 2b04:d006 -a 0 -s 0x8020000 -D system-part1-@FW_VER@-photon.bin +dfu-util -d 2b04:d006 -a 0 -s 0x8060000:leave -D system-part2-@FW_VER@-photon.bin + +// P1 +dfu-util -d 2b04:d008 -a 0 -s 0x8020000 -D system-part1-@FW_VER@-p1.bin +dfu-util -d 2b04:d008 -a 0 -s 0x8060000:leave -D system-part2-@FW_VER@-p1.bin + +// Electron +dfu-util -d 2b04:d00a -a 0 -s 0x8020000 -D system-part1-@FW_VER@-electron.bin +dfu-util -d 2b04:d00a -a 0 -s 0x8040000:leave -D system-part2-@FW_VER@-electron.bin +``` +##### @ELECTRON_PARTS@2endif + +##### @ELECTRON_PARTS@3if +``` +The local DFU-UTIL method + +// Core +dfu-util -d 1d50:607f -a 0 -s 0x8005000:leave -D tinker-@FW_VER@-core.bin + +// Photon +dfu-util -d 2b04:d006 -a 0 -s 0x8020000 -D system-part1-@FW_VER@-photon.bin +dfu-util -d 2b04:d006 -a 0 -s 0x8060000:leave -D system-part2-@FW_VER@-photon.bin + +// P1 +dfu-util -d 2b04:d008 -a 0 -s 0x8020000 -D system-part1-@FW_VER@-p1.bin +dfu-util -d 2b04:d008 -a 0 -s 0x8060000:leave -D system-part2-@FW_VER@-p1.bin + +// Electron +dfu-util -d 2b04:d00a -a 0 -s 0x8060000 -D system-part1-@FW_VER@-electron.bin +dfu-util -d 2b04:d00a -a 0 -s 0x8020000 -D system-part2-@FW_VER@-electron.bin +dfu-util -d 2b04:d00a -a 0 -s 0x8040000:leave -D system-part3-@FW_VER@-electron.bin +``` +##### @ELECTRON_PARTS@3endif + +--- + +**Downgrading from @FW_VER@ to current default firmware** + +##### @FW_VER@0.5.1if +**Caution:** After upgrading to 0.5.1, DO NOT downgrade system firmware via OTA remotely! This will cause Wi-Fi credentials to be erased on the Photon and P1. This does not affect the Core or Electron. Feel free to downgrade locally with the understanding that you will have to re-enter Wi-Fi credentials. Also note that 0.5.1 fixes several important bugs, so there should be no reason you'd normally want to downgrade. +##### @FW_VER@0.5.1endif + +##### @FW_VER@0.5.2if +**Note:** Upgrading to 0.5.2 will now allow you to downgrade remotely OTA to v0.5.0 or earlier without erasing Wi-Fi credentials. There are still some cases where a downgrade will erase credentials, but only if you have explicitly set the country code to something other than the `default` or `JP2`. For example, if you set the country code to `GB0` or `US4`, if you downgrade to v0.5.0 your Wi-Fi credentials will be erased. Leaving the country code at `default` or set to `JP2` will not erase credentials when downgrading to v0.5.0. **Do not** downgrade to v0.5.1 first, and then v0.5.0... this will erase credentials in all cases. +##### @FW_VER@0.5.2endif + +Current defaults system firmware would be the latest non-rc.x firmware version. E.g. if the current list of default releases was 0.5.1, 0.5.2, **0.5.3** (would be the latest). + +The easiest way to downgrade from a System Firmware Version @FW_VER@ is to use the Particle CLI with a single command. You will first put the Tinker back on the device, then downgrade the System Firmware. Running the commands in this order prevents the device from automatically re-upgrading (based on user app version dependencies) after downgrading. This will **require a CLI version associated with your desired default firmware**. To determine which version to use, click on the default version desired in the table under [Programming and Debugging Notes](#programming-and-debugging-notes) and refer to the CLI version required in **The easy local method using Particle CLI** section. + +If you have the [Particle CLI](https://github.com/spark/particle-cli) installed already, you can install a specific version like v1.16.0 with the following command `sudo npm update -g particle-cli@v1.16.0` (note: you can try without sudo first if you wish). Replace v1.16.0 with your desired version. + +To downgrade system firmware, make sure the device is in [DFU mode](http://docs.particle.io/photon/modes/#selecting-various-modes-dfu-mode-device-firmware-upgrade) (flashing yellow LED) and run these commands in order: + +``` +Downgrading from @FW_VER@ to current default firmware + +1) Make sure Tinker is installed, instead of a @FW_VER@ app that you may currently have running on your device. Have the device in DFU mode and run: + +particle flash --usb tinker + +2) Make sure the device is in DFU mode and run: + +particle update +``` + +**Note:** The CLI and `particle update` command is only updated when default firmware versions are released. This is why we install a specific version of the CLI to get a specific older version of default firmware. + +--- + +**Debugging for Electron** + +##### Instructions on using the Tinker USB Debugging app [are here](https://docs.google.com/document/d/1NdYxPPk_i_mM2wM9oSbSZB1ElDlHA_x-IHY-UC7w62M/edit?usp=sharing) +This is useful for simply capturing the Electron's connection process. + +--- + +##### Instructions on using the Electron Troubleshooting app [are here](https://docs.google.com/document/d/1U_Wzy2SPRC3hZnKtw8d6QN2Tm8Q7QwtEbSUAeTkO3bk/edit?usp=sharing) +This is useful for interacting with the Electron's connection process. + +#### release-notes-wrapper