Webroots 2: Cloud Streaming

Now that we've got all our wireless programming tools set up, we will begin to interface with the Night Light directly. Say I want a different color? Fiddling with the RGB values is fun up to a certain point. This tutorial will teach you how to set up a service in the Adafruit IO to change the light from your phone.

We will utilise the Adafruit IO since it is very easy to set up and use.

  • Register for an Adafruit account
  • Sign in for the Adafruit IO Beta
  • Install the Adafruit MQTT library & Adafruit IO Library
  • Install the AdruinoHttpClient library

Dashboard Setup

Adafruit's information interfaces is separated into Dashboards. All the information you ever want for a project is stored in a Dashboard. It is similar to Workspaces in MacOS or in Windows. Within each workspace, information is displayed in Streams. Your application provides data to a channel or Stream and it is then displayed in the widget.

Setting up a Dashboard is simple. Navigate to Dashboards in Adafruit IO and under actions, Create a New Dashboard. You can name this dashboard "Night Light". Clicking on it will lead you to the next screen.

Block Setup

The screen will be black with a few symbols. You populate your dashboard with dials and controls and displays to represent your project. Click on the blue + and select the Color Picker. In the next screen, create a new feed named "Nightlight_color". Select the feed by clicking the checkbox next to the name and proceed on to the next step.

Figure 1. Selecting Feed

Click on Create block in the next screen.

Figure 2. Block created

Now this block will publish information to the 'Nightlight_color' stream. We will program the night light to listen to this stream. It will then pick up on any changes in the stream and hopefully, change color!

Arduino Setup

You will need an API key. This key uniquely identifies you to the Adafruit servers so that they know what content to serve you. In Adafruit IO, go to Settings > View AIO Key. Take note of the Active Key as that is what you will need to supply to your code in Arduino later.

You will also need to install the Adafruit IO library.

Figure 3. Adafruit IO library

Under Files > Examples > Adafruit IO Arduino > adafruitio_14_neopixel

As per earlier projects, we will only be extracting useful code. Under config.h you will see the following code. These can be added to the start of your project:

#define IO_USERNAME    "your_username" //Your Adafruit username
#define IO_KEY         "your_key" //AIO key from earlier
#define WIFI_SSID       "your_ssid"
#define WIFI_PASS       "your_pass"

#include "AdafruitIO_WiFi.h"

AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS); // create Adafruit io object.

Under adafruitio_14_neopixel you will see the following code, extract them into your own project. We will set up the RGB LEDs as follows

#include "Adafruit_NeoPixel.h"

#define PIXEL_PIN     D6 // pin we are connected 
#define PIXEL_COUNT   12
#define PIXEL_TYPE    NEO_GRB + NEO_KHZ800

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);

And we set up the WeMos to listen to the feed that we have prepared in Adafruit IO earlier:

AdafruitIO_Feed *color = io.feed("Nightlight_color");

Under setup:

Serial.begin(115200); // begin serial communications for debugging

io.connect(); // This is a method under AdafruitWifi.h that helps to set up the wireless connection and contacts the IO server.

color->onMessage(handleMessage); // we will need to handle messages on the "Nightlight_color" stream that we have set up earlier

while(io.status() < AIO_CONNECTED) {
  Serial.print(".");
  delay(500);
}
Serial.println();
Serial.println(io.statusText());

pixels.begin(); //remember to initialise the RGB LEDs.
pixels.show();

Under loop:

io.run(); 

This is an asynchronous function that does not block processing like what delay does. It will be called periodically by an internal timer that is set up and contact the Adafruit servers, asking if there was any change in the data. If there is a change, it will then check what streams we have set up to watch for, and trigger it via an interrupt.

These functions look different from the interrupts that we have learnt earlier because Adafruit has simplified the interface. However, the characteristics of interrupts still remain: no return values, and no modifying of non-volatile values.

We also have to include another function that we called earlier. The handleMessage function. It essentially processes the incoming data that is a hexadecimal value that is used to represent colors. However the pixels.setPixelColor function does not understand this, and so Adafruit has kindly written a function that converts hexdecimal to decimal, which we can then put into the pixels.setPixelColor function:

void handleMessage(AdafruitIO_Data *data) { //New AdafruitIO_Data object!

  // print RGB values and hex value
  Serial.println("Received HEX: ");
  Serial.println(data->value()); // displays data as hex

  long color = data->toNeoPixel(); // converts hex to a value the neopixel can use!

  for(int i=0; i<PIXEL_COUNT; ++i) {
    pixels.setPixelColor(i, color); // sets the color.
  }
  pixels.show();

}

Note that you cannot use functions such as value() or toNeoPixel() anywhere else as they are functions that belong to the AdafruitIO_Data object. This is like saying that the multiply * function belongs to the int object. Applying it to a string or char object will not make sense. Objects are how things are defined. It is a certain kind of 'thing' that is different from the others, even if in name.

Sketch

The code is available on GitHub.

#include <Adafruit_NeoPixel.h>
#define NUMPIXELS 1 // change if you have more pixels
#define PIN D6 // config for ESP8266
#define INPUT_PIN A0 // config for ESP8266
#define BRIGHTNESS ... //set this as the threshold value for darkness

#define IO_USERNAME    "your_username" //Your Adafruit username
#define IO_KEY         "your_key" //AIO key from earlier
#define WIFI_SSID       "your_ssid"
#define WIFI_PASS       "your_pass"

#include "AdafruitIO_WiFi.h"

AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS); // create Adafruit io object.

AdafruitIO_Feed *color = io.feed("Nightlight_color");  

// Set the volatile keyword if you want to retain the RGB values

int volatile r = 0;  
int volatile g = 0;  
int volatile b = 0;  
int input_value = 0;

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); 

// setup and loop functions

void setup() {
  Serial.begin(115200); // begin serial communications for debugging

  io.connect(); // This is a method under AdafruitWifi.h that helps to set up the wireless connection and contacts the IO server.

  color->onMessage(handleMessage); //we will need to handle messages on the "Nightlight_color" stream that we have set up earlier

  while(io.status() < AIO_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();
  Serial.println(io.statusText()); 
  

  // Init RGB led
 
  pixels.begin();
  pixels.show();
  
  pinMode(INPUT_PIN, INPUT);

  r=100; // set RGB values, moving them out of turn_on()
  g=30;
  b=70;
}

void loop() {  
  // put your main code here, to run repeatedly:
  io.run(); 
  input_value = analogRead(INPUT_PIN);
  if (input_value < BRIGHTNESS) {
    turn_on();
  }
  if (input_value > BRIGHTNESS) {
    turn_off();
  }
  delay(100);
}


// Functions we wrote

void turn_on(){  

  for(int i=0; i <NUMPIXELS;i++){
    pixels.setPixelColor(i,pixels.Color(r,g,b));
  }
  pixels.show();
}

void turn_off(){  
  for(int i=0; i<NUMPIXELS;i++){
    pixels.setPixelColor(i,pixels.Color(0,0,0)); // set the color to (0,0,0), off!
  }
  pixels.show();
}

// New functions

void handleMessage(AdafruitIO_Data *data) { //New AdafruitIO_Data object!

  // print RGB values and hex value
  Serial.println("Received HEX: ");
  Serial.println(data->value()); // displays data as hex

  //Calls hex2dec function and updates global volatile rgb values.
  hex2dec(data->value());

  long color = data->toNeoPixel(); // converts hex to a value the neopixel can use!

  for(int i=0; i<NUMPIXELS; ++i) {
    pixels.setPixelColor(i, color); // sets the color.
  }
  pixels.show();

}

void hex2dec(string hexstring){
  int number = strtol( &hexstring[1], NULL, 16);

  // Split them up into r, g, b values
  r = number >> 16;
  g = number >> 8 & 0xFF;
  b = number & 0xFF;
}