Controlling Hardware From a Dashboard

In this section we'll set up a dashboard on Adadfruit IO to control a servo and neopixels on the Feather/Crickit.

Setting up Adafruit IO

Let's start by connecting the servo and NeoPixels to Adafruit IO. If you aren't familiar with feeds, we have a guide for that.

We start by creating a new feed group called crickit (Feeds -> Actions -> Create New Group). We'll use that to contain the feeds for this guide.  Now we can add the feeds: neopixel-control and servo1-control via Feeds -> Actions -> Create a new Feed. When you create each, you have the option of adding them to a group. Do so, adding them to the crickit group.

Arduino Code

The first program is called dashboard_control.ino. This program will allow you to control a servo and NeoPixels connected on an Adafruit Crickit Featherwing via Adafruit IO.

The key parts of the code are setting up the feeds and handling events from them.

Code Highlights

Setting up the feeds is split into two parts: declaring them, and initializing them. The declarations are simple: make variables that are pointers to AdafruitIO_Feed instances.

Download: file
AdafruitIO_Feed *servo1_control;
AdafruitIO_Feed *neopixel_control;

There's a function to allocate/initialize the feeds. It's called near the start of the setup() function.

Download: file
void setup_feeds()
{
  servo1_control = io.feed("crickit.servo1-control");
  neopixel_control = io.feed("crickit.neopixel-control");
}

Notice that the feed names have a crickit. prefix. That's because they are in the crickit feed group. Now the connection to Adafruit IO can be started.

In config.h the AdafruitIO interface object is created, in our case using WiFi:

Download: file
AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS);

Once the feeds have been created, the Adafruit IO interface object can be initialized. This will make the connection to Adafruit IO and get it running.

Download: file
  io.connect();

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

Setting up the hardware

Now that Adafruit IO is set up, we can turn to the Crickit and it's connected hardware.

Download: file
Adafruit_Crickit crickit;
seesaw_Servo servo_1(&crickit);
seesaw_NeoPixel strip = seesaw_NeoPixel(NEOPIX_NUMBER_OF_PIXELS, NEOPIX_PIN, NEO_GRB + NEO_KHZ800);

And we initialize them at the end of setup():

Download: file
void setup()
{
  ...
  servo1_control->get();
  servo_1.attach(CRICKIT_SERVO1, 600, 2400);
}

The attach function literally attaches the servo object to a specific servo port on the Crickit, servo 1, in this case.

The attach function 'attaches' the servo object to a specific servo port on the Crickit. servo 1, in this case.

 

Be sure to plug in your servo into the right slot, and with the white/yellow wire next to the 1 symbol

Loop()

In this sketch, the loop() function is does the bare minimum: it keeps the connection to Adafruit IO alive and well and routes any incoming events to the associated handler function.

Download: file
void loop()
{
  // io.run(); is required for all sketches.
  // it should always be present at the top of your loop
  // function. it keeps the client connected to
  // io.adafruit.com, and processes any incoming data.
  io.run();
}

Event handlers

Back in setup(), after the connection to Adafruit IO and the feeds are in place the event handlers can be set.

Download: file
  servo1_control->onMessage(handle_servo_message);
  neopixel_control->onMessage(handle_neopixel_message);

This attaches an event handler function to each of the Adafruit IO feeds that we want to respond to: handle_servo_message handles servo angle events, and handle_neopixel_message handles NeoPixel color changes.

Download: file
void handle_servo_message(AdafruitIO_Data *data)
{
  Serial.print("received servo control <- ");
  Serial.println(data->value());
  int angle = data->toInt();
  if(angle < 0) {
    angle = 0;
  } else if(angle > 180) {
    angle = 180;
  }
  servo_1.write(angle);

}

void handle_neopixel_message(AdafruitIO_Data *data)
{
  Serial.print("received neopixel control <- ");
  Serial.println(data->value());
  long color = data->toNeoPixel();
  for (int pixel = 0; pixel < NEOPIX_NUMBER_OF_PIXELS; pixel++) {
    strip.setPixelColor(pixel, color);
  }
  strip.show();
}

That's it: set everything up and attach even handlers. This sketch is completely reactive, responding to events by making changes to the hardware.

Generating events

Now that the Feather and Crickit are set up and the sketch in place, we need to send it events to handle. We'll make an Adafruit IO dashboard for this. If you haven't worked with dashboards, there's a guide to get you started.

We'll want a number slider block connected to the servo1-control feed that has a value range of 0 to 180. For neopixel-control we need to make a color picker block.

If everything worked, you should be able to power up the Feather/Crickit and change the dashboard controls to see the servo rotate and the NeoPixels change color.

You can use the Arduino serial monitor to monitor the status messages from the sketch, just in case the board does not make a connection first to the WiFi network and then to Adafruit IO.

The complete sketch is below:

// Crickit + Adafruit IO Subscribe Example
//
// Adafruit invests time and resources providing this open source code.
// Please support Adafruit and open source hardware by purchasing
// products from Adafruit!
//
// Written by Dave Astels for Adafruit Industries
// Copyright (c) 2018 Adafruit Industries
// Licensed under the MIT license.
//
// All text above must be included in any redistribution.

/************************** Configuration ***********************************/

// edit the config.h tab and enter your Adafruit IO credentials
// and any additional configuration needed for WiFi, cellular,
// or ethernet clients.
#include "config.h"
#include <Adafruit_Crickit.h>
#include <seesaw_servo.h>
#include <seesaw_neopixel.h>

#define NEOPIX_PIN (20)                  /* Neopixel pin */
#define NEOPIX_NUMBER_OF_PIXELS (7)


// set up the feeds
AdafruitIO_Feed *servo1_control;
AdafruitIO_Feed *neopixel_control;

// set up the Crickit

Adafruit_Crickit crickit;
seesaw_Servo servo_1(&crickit);  // create servo object to control a servo

// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
seesaw_NeoPixel strip = seesaw_NeoPixel(NEOPIX_NUMBER_OF_PIXELS, NEOPIX_PIN, NEO_GRB + NEO_KHZ800);

void setup_feeds()
{
  servo1_control = io.feed("crickit.servo1-control");
  neopixel_control = io.feed("crickit.neopixel-control");
}


void setup()
{
  // start the serial connection
  Serial.begin(115200);

  // wait for serial monitor to open
  while(! Serial);

  setup_feeds();
  Serial.println("Feeds set up");

  Serial.println("Connecting to Adafruit IO");

  // connect to io.adafruit.com
  io.connect();

  // set up message handlers for the servo and neopixel feeds.
  // the handle_*_message functions (defined below)
  // will be called whenever a message is
  // received from adafruit io.

  servo1_control->onMessage(handle_servo_message);
  neopixel_control->onMessage(handle_neopixel_message);

  // wait for a connection
  while(io.status() < AIO_CONNECTED) {
    Serial.print(".");
    //    Serial.println(io.statusText());
    delay(500);
  }

  // we are connected
  Serial.println();
  Serial.println(io.statusText());

  if (!crickit.begin()) {
    Serial.println("Error starting Crickit!");
    while(1);
  } else {
    Serial.println("Crickit started");
  }

  if(!strip.begin()){
    Serial.println("Error starting Neopixels!");
    while(1);
  } else {
    Serial.println("Neopixels started");
  }

  servo1_control->get();

  servo_1.attach(CRICKIT_SERVO1);

  Serial.println("setup complete");
}


void loop()
{

  // io.run(); is required for all sketches.
  // it should always be present at the top of your loop
  // function. it keeps the client connected to
  // io.adafruit.com, and processes any incoming data.
  io.run();
}


void handle_servo_message(AdafruitIO_Data *data)
{
  Serial.print("received servo control <- ");
  Serial.println(data->value());
  int angle = data->toInt();
  if(angle < 0) {
    angle = 0;
  } else if(angle > 180) {
    angle = 180;
  }
  servo_1.write(angle);

}


void handle_neopixel_message(AdafruitIO_Data *data)
{
  Serial.print("received neopixel control <- ");
  Serial.println(data->value());
  long color = data->toNeoPixel();
  for (int pixel = 0; pixel < NEOPIX_NUMBER_OF_PIXELS; pixel++) {
    strip.setPixelColor(pixel, color);
  }
  strip.show();
}
This guide was first published on Jul 18, 2018. It was last updated on Jul 18, 2018. This page (Controlling Hardware From a Dashboard) was last updated on Sep 23, 2019.