sample_pir-lights_rgb

Dependencies:   ChainableLED

docs/4_connectivity.md

Committer:
iv123
Date:
2017-06-18
Revision:
0:7a352727249b

File content as of revision 0:7a352727249b:

# Adding connectivity

Now that we've built our basic circuit and wrote the code to control that circuit, we can start adding connectivity to the project. Part of the ARM mbed IoT Device Platform is mbed Device Connector, a unified solution to connect devices to the internet and communicate with them regardless of *how* these devices connect to the internet. There are libraries available for a variety of connectivity methods, including Ethernet, Wi-Fi, 6LoWPAN, Thread and Cellular.

## Obtaining a device certificate

All data that goes from the device to mbed Device Connector (and vice-versa) is end-to-end encrypted by [mbed TLS](https://tls.mbed.org). To set up secure communications we need a security certificate. We can get one from the mbed Device Connector website:

1. Go to the [mbed Device Connector homepage](https://connector.mbed.com) and click *Sign in to get connected*.
1. If prompted for your login credentials, use your mbed developer account (the same account you use to log into the mbed Online Compiler).
1. Go to *My Devices* > *Security Credentials*.
1. Click *Get my security credentials*.
1. Copy the contents of the gray box. This is your certificate.

    <span class="images">![The certificate is located in the gray box](assets/lights16.png)</span>

1. Go back to the online compiler.
1. Create a new file ``security.h`` in your application's ``source`` directory. 
1. Paste the certificate into this file.

## Adding connectivity to the board

### Ethernet

If you have a development board that connects over Ethernet, just plug in an Ethernet cable. We’re assuming that the network has DHCP enabled and the firewall does not block connections to *http://connector.mbed.com*.

### ESP8266 Wi-Fi module

To wire the ESP8266 module up to your development board, look at the [ESP8266 Cookbook page](https://developer.mbed.org/users/4180_1/notebook/using-the-esp8266-with-the-mbed-lpc1768/). Broadly, this means hooking up the ESP8266's TX pin to `D0` and RX pin to `D1`.

<span class="notes">**Note about ESP8266 on NUCLEO boards:** On the NUCLEO boards pins `D0` and `D1` are used for serial communication with the computer. Use pins `D8` and `D2` instead.</span>

### 6LoWPAN

First connect your 6LoWPAN gateway to an IPv6-enabled network by following the steps under 'Gateway Configuration' on [this page](https://github.com/ARMmbed/mbed-client-example-6lowpan#gateway-configuration). 

Then attach the 6LoWPAN shield to the top of your development board.

## Adding libraries with the online compiler

For the device and mbed Device Connector to talk we need the [mbed Client library](https://docs.mbed.com/docs/mbed-client-guide/en/latest/). This library is already included in mbed OS, and is very powerful, but can also be daunting for new users. In this example we'll use an additional library built on top of mbed Client: SimpleClient. This library is designed to easily expose variables and resources to the cloud.

We will also use [EasyConnect](https://github.com/ARMmbed/easy-connect) to handle connectivity.

To add these libraries to your project:

1. Go back to the online compiler.
1. Right click on your program in the tree and select *Import Library* > *From URL*.
1. Under *Source URL* enter: ``https://github.com/armmbed/easy-connect``.
1. Do **not** tick 'Update all sub-libraries to the latest version'.
1. Click *Import*.
1. Again, right click on your program and select *Import Library* > *From URL*.
1. Under *Source URL* enter: ``https://developer.mbed.org/teams/sandbox/code/simple-mbed-client/``.
1. Click *Import*.

## Adding libraries with mbed CLI

If you are using mbed CLI, run the following commands to add the libraries:

```bash
$ mbed add easy-connect
$ mbed add http://developer.mbed.org/teams/sandbox/code/simple-mbed-client/
```

## Updating configuration

We need to tell EasyConnect which connectivity method to use. Open ``mbed_app.json`` and locate the `network-interface` field. Change the `value` to the connectivity method used:

```json
/* mbed_app.json */

/* snip */

        "network-interface":{
            "help": "options are ETHERNET,WIFI_ESP8266,MESH_LOWPAN_ND,MESH_THREAD",
            "value": "ETHERNET"
        },
        "esp8266-tx": {
            "help": "Pin used as TX (connects to ESP8266 RX)",
            "value": "D1"
        },
        "esp8266-rx": {
            "help": "Pin used as RX (connects to ESP8266 TX)",
            "value": "D0"
        },
        "esp8266-ssid": {
            "value": "\"SSID\""
        },
        "esp8266-password": {
            "value": "\"Password\""
        },
        "esp8266-debug": {
            "value": true
        }
    }

/* snip */
```

If you:

* Are using Wi-Fi: also set your Wi-Fi SSID and your password. 
* Used pins other than `D0`/`D1`: also change the pin names.

## Writing code

### Setting up a connection

We need to add some code to the application so it connects to the internet and sets up a connection to mbed Device Connector.

Replace ``main.cpp`` with:

```cpp
#include "mbed.h"
#include "led.h"        // Abstracts away the differens between the LED types
#include "security.h"   // Security configuration
#include "easy-connect.h"
#include "simple-mbed-client.h"

EventQueue eventQueue;  // An event queue
Thread eventThread;     // An RTOS thread to process events in

SimpleMbedClient client;

// PIR sensor acts as an interrupt - signals us whenever it goes high (or low)
InterruptIn pir(PIR_PIN);   // This pin value comes out mbed_app.json


// YOUR CODE HERE
void pir_rise() { }
// END OF YOUR CODE HERE

// Use the built-in LED as a status LED
DigitalOut statusLed(LED1);
int        statusLedBlinkId;    // Callback ID
void blink_builtin_led() {
  statusLed = !statusLed;
}

void registered() {
  // When we registered with mbed Device Connector, blink faster
  eventQueue.cancel(statusLedBlinkId);

  statusLedBlinkId = eventQueue.call_every(300, &blink_builtin_led);
}

int main(int, char**) {
  // Using an event queue is a very useful abstraction around many microcontroller 'problems', like dealing with ISRs
  // see https://developer.mbed.org/blog/entry/Simplify-your-code-with-mbed-events/
  eventThread.start(callback(&eventQueue, &EventQueue::dispatch_forever));

  // Blink the built-in LED every 1000ms. After registering we'll blink every 300ms.
  statusLedBlinkId = eventQueue.call_every(1000, &blink_builtin_led);

  // Disable the LED
  setRgbColor(0.0f, 0.0f, 0.0f);

  // The PIR sensor uses interrupts, no need to poll
  pir.rise(eventQueue.event(&pir_rise));

  NetworkInterface* network = easy_connect(true);
  if (!network) {
    printf("Connect to internet failed... See serial output.\r\n");
    return 1;
  }

  struct MbedClientOptions options = client.get_default_options();
  options.DeviceType = "light-system";
  if (!client.setup(options, network)) {
    printf("Setting up mbed_client failed...\r\n");
    return 1;
  }

  client.on_registered(eventQueue.event(&registered));

  // We can just let the main thread end here, no need to busy-loop
}
```

### Program logic

The code sample above does not do much, except for setting up the connection. We can now define some logic for this program:

1. The color of the LED should be configurable.
1. The period between the moment of motion detection to the moment lights go out should be configurable.
1. There should be a permanent-on mode for the lights.
1. We should notify mbed Device Connector whenever we detect movement.

We can implement these actions by defining *resources*: pieces of information the device makes available. We can read or write to them from the cloud, and the device can use a resource's value to determine the correct action to perform. We can reach a resource with a URI and access modifier (for example, only write allowed), and we can also subscribe to them, so we'll be notified when a resource changes.

Let's define a resource for each of our actions:

* `led/0/color` - the color of the LED.
* `led/0/timeout` - the timeout (in seconds) after detection; lights are disabled when this period ends.
* `led/0/permanent_status` - whether we should have the lights permanently on (or off).
* `pir/0/count` - the number of times the PIR sensor was triggered. Read only, and should allow notifications.

We can use SimpleClient to define these resources and attach actions to each resource.

Replace the following section in ``main.cpp``:

```cpp
// YOUR CODE HERE
void pir_rise() { }
// END OF YOUR CODE HERE
```

with (comments inline):

```cpp
// Fwd declaration
void putLightsOn();
void colorChanged(int newColor);

// Variable that holds whether the light is on because the PIR sensor triggered (and timeout didn't happen yet)
bool ledOnBecauseOfPir = false;

// Timeout based on led/0/timeout, disables the light after a set interval
Timeout pirTimeout;

// Permanent statuses (set by led/0/permanent_status)
#define STATUS_NONE     0
#define STATUS_ON       1
#define STATUS_OFF      2

// clear the lights
void putLightsOff() {
  setRgbColor(0.0f, 0.0f, 0.0f);
}

// Status changes
void statusChanged(int newStatus) {
  switch (newStatus) {
    case STATUS_ON: // Permanently on? OK.
      putLightsOn();
      break;
    case STATUS_NONE: // Otherwise listen to PIR sensor
    case STATUS_OFF:  // Or be off forever
      putLightsOff();
      break;
  }
}

// Here are our resources:
// We encode color in 3 bytes [R, G, B] and put it in an integer by providing the color as an hex value (default color: green)
SimpleResourceInt ledColor = client.define_resource("led/0/color", 0x00ff00, &colorChanged);
SimpleResourceInt ledTimeout = client.define_resource("led/0/timeout", 5);
SimpleResourceInt ledStatus = client.define_resource("led/0/permanent_status", STATUS_NONE, &statusChanged);
SimpleResourceInt pirCount = client.define_resource("pir/0/count", 0, M2MBase::GET_ALLOWED);

// As said above, color is encoded in three bytes
void putLightsOn() {
  // parse the individual channels
  int redCh   = ledColor >> 16 & 0xff;
  int greenCh = ledColor >> 8 & 0xff;
  int blueCh  = ledColor & 0xff;

  // our color is 0..255, but we need a float between 0..1, cast it.
  float red = static_cast<float>(redCh) / 255.0f;
  float green = static_cast<float>(greenCh) / 255.0f;
  float blue = static_cast<float>(blueCh) / 255.0f;
  setRgbColor(red, green, blue);
}

// Color updated from the cloud,
// if the LED is on because of the PIR, or if the LED is on permanently -> Set the color.
void colorChanged(int newColor) {
  if (ledOnBecauseOfPir || ledStatus == STATUS_ON) {
    putLightsOn();
  }
}

// Timeout (from led/0/timeout) happened after PIR sensor was triggered...
void onPirTimeout() {
  // if we're not permanent on
  if (ledStatus != STATUS_ON) {
    // clear the lights
    putLightsOff();

    ledOnBecauseOfPir = false;
  }
}

// When the PIR sensor fires...
void pir_rise() {
  // Update the resource
  pirCount = pirCount + 1;

  // Permanent off? Don't put the lights on...
  if (ledStatus == STATUS_OFF) return;

  // Otherwise do it!
  ledOnBecauseOfPir = true;
  putLightsOn();

  // And attach the timeout
  pirTimeout.attach(eventQueue.event(&onPirTimeout), static_cast<float>(ledTimeout));
}
```

When you compile and flash this program, you'll see that when you wave your hand in front of the PIR sensor the color of the LED changes to green, and the LED always goes off after 5 seconds.

When the connection to mbed Device Connector is created, the onboard LED will blink faster. We can now control this device from the cloud.

<span class="notes">**Note:** No connection? [Inspect the logs on the device](https://developer.mbed.org/handbook/SerialPC#host-interface-and-terminal-applications).</span>