Travis Rosenbaum
Published © GPL3+

Olive - Your Animatronic Home Assistant

Olive is a home voice assistant you can actually look in the eye! Animatronic features allow her to have a real presence in your home!

IntermediateFull instructions provided20 hours11,078
Olive - Your Animatronic Home Assistant

Things used in this project

Hardware components

Sterilite Flip Top Clear Storage Box
I had this container, and it was the perfect size for housing all the components. The flip-top mechanism makes tweaks and repairs super convenient.
×1
Iris Mechanism (or other animatronics)
Any sort of iris mechanism will work, as long as you can control opening/closing it via the motor controller. I got Olive's Iris from a surplus X-Ray Machine parts sale, and an optical parts store, but you can also do well a 3D Printer.
×1
NeoPixel Ring: WS2812 5050 RGB LED
Adafruit NeoPixel Ring: WS2812 5050 RGB LED
Olive's colorful iris lighting, I use the 16-LED RGBW version.
×1
Adafruit Single NeoPixel
This is for the pupil interior coloring.
×1
Adafruit DRV8833 DC/Stepper Motor Driver Breakout Board
This thing has so many functions in such a tiny board, overkill for a single bidirectional motor but it's $5 so why not?
×1
DSTIKE NodeMCU-07 V2 (ESP8266, ESP-07)
This is a carrier board for the venerable ESP8266 ESP-07. It handles animatronics and lighting control duties. It has an MQTT Listener which triggers animations directly from Snips MQTT events.
×1
Raspberry Pi Zero Wireless
Raspberry Pi Zero Wireless
The Satellite unit which lives inside the Eye and runs the Snips Audio Server.
×1
ReSpeaker Mic Array v2.0
Seeed Studio ReSpeaker Mic Array v2.0
This acts as the ears for your device--I really like the direction indicators which make it seem like much more "active" listening. You can also use any other decent microphone array, such as the PS3 Eye unit I used for earlier prototyping.
×1
Adafruit Speaker Bonnet
This is like a sound card for your Pi, once soldered it fits on the GPIO expander headers.
×1
Raspberry Pi 3 Model B+
Raspberry Pi 3 Model B+
The main Hub which runs Snips and runs the Assistant's skills code.
×1

Software apps and online services

Raspbian
Raspberry Pi Raspbian
Raspbian Lite
Snips.ai - Voice Assistant SDK
The ears and mouth of the system.

Hand tools and fabrication machines

Cordless Drill
Screwdriver / Drill bits
Hot glue gun (generic)
Hot glue gun (generic)
I used to really look down on glue guns, but they are excellent for securing jumper wires, and mounting NeoPixels to diffusers.
Hobby Blade / X-Acto / Box Cutter
Useful for shaving plastic cuts, or cutting foam.
Soldering iron (generic)
Soldering iron (generic)
Soldering may be needed for wires and connectors.

Story

Read more

Schematics

Olive - Fritzing Breadboard/Schematic

This is the breadboard layout and schematic for Olive's Microcontroller system (X-Ray Iris Motor version). I was able to utilize the power headers on the Speaker Bonnet to avoid needing a breadboard for power routing.

Olive Breadboard

Snapshot of Fritzing Breadboard

Olive Schematic

Snapshot of Fritzing Schematic

Olive Mini Breadboard (Servo Version)

Snapshot of Fritzing Breadboard for Olive Mini variant.

Olive Mini Schematic (Servo Version)

Snapshot of Fritzing Schematic for Olive Mini (Servo Variant)

Code

Olive Eye Controller (X-Ray Iris Motor Version)

C/C++
Microcontroller Code for the main Olive Unit (with the X-Ray Collimator Iris). This will work for any kind of motor + potentiometer-style feedback. It also implements smoothing on the analog potentiometer input to reduce some of the jitter.
/*
 * Olive - Eye Controller
 * Author: Travis Rosenbaum (TRosenbaum@gmail.com)
 */

// D-Duino Pinouts
#define D0 16
#define D1 5
#define D2 4
#define D3 0
#define D4 2
#define D5 14
#define D6 12
#define D7 13
#define D8 15
#define TX 1
#define RX 3
#define SDA_PIN D7
#define SCL_PIN D6
 
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager
#include <WiFiUdp.h>

// NeoPixel (WS2812B)
#include <Adafruit_NeoPixel.h>
// Iris LED Setup
#define IRIS_NUMPIXELS 16 // Number of pixels in the Iris ring
#define IRIS_PIXEL_PIN D8
Adafruit_NeoPixel iris = Adafruit_NeoPixel(IRIS_NUMPIXELS, IRIS_PIXEL_PIN, NEO_GRBW + NEO_KHZ800);

// Pupil LED Setup
#define PUPIL_PIXEL_PIN D4
Adafruit_NeoPixel pupil = Adafruit_NeoPixel(1, PUPIL_PIXEL_PIN, NEO_GRB + NEO_KHZ800);

// MQTT Client
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"

// Iris Motor contoller pins
#define AIN1 D6 // CLOSE - AIN1
#define AIN2 D7 // OPEN - AIN2

// MQTT Client
#define MQTT_SERVER      "192.168.1.77"
#define MQTT_SERVERPORT  1883 // 1883 for MQTT, 8883 for MQTTS
#define MQTT_CLIENTID    "olive-eye-1"
#define MQTT_USERNAME    "olive-eye"
#define MQTT_KEY         "key"
//
//// Use WiFiClientSecure for SSL/TLS support
WiFiClient client;
Adafruit_MQTT_Client mqtt(&client, MQTT_SERVER, MQTT_SERVERPORT, MQTT_USERNAME, MQTT_KEY);

/* Hermes - relevant topics:
 *  hermes/hotword/toggleOff                - Hotword detection off (usually when hotword detected, but also when disabled)
 *  hermes/dialogueManager/sessionStarted   - Conversation / listening begins
 *  hermes/asr/textCaptured                 - Text was captured from speech
 *  hermes/nlu/intentParsed                 - The intent was understood, now we wait for the activity
 *  hermes/nlu/intentNotRecognized          - "Man, ain't nobody understand the words that are coming out of your mouth."
 *  hermes/dialogueManager/endSession       - (Optional) The session-ending activity / response.
 *  hermes/dialogueManager/sessionEnded     - The definitive end of the conversation.
 *  hermes/hotword/toggleOn                 - Back to listening for hotword
 *  ----------------------------------------------------------------------------------------------
 *  Other topics:
 *  hermes/intent/TRosenbaum:openPodBayDoors - Olive 9000 Mode
 */
 
Adafruit_MQTT_Subscribe toggleOffSub            = Adafruit_MQTT_Subscribe(&mqtt, "hermes/hotword/toggleOff"                 , MQTT_QOS_1);
//Adafruit_MQTT_Subscribe sessionStartedSub       = Adafruit_MQTT_Subscribe(&mqtt, "hermes/dialogueManager/sessionStarted"    , MQTT_QOS_1);
Adafruit_MQTT_Subscribe textCapturedSub         = Adafruit_MQTT_Subscribe(&mqtt, "hermes/asr/textCaptured"                  , MQTT_QOS_1);
//Adafruit_MQTT_Subscribe intentParsedSub         = Adafruit_MQTT_Subscribe(&mqtt, "hermes/nlu/intentParsed"                  , MQTT_QOS_1);
//Adafruit_MQTT_Subscribe intentNotRecognizedSub  = Adafruit_MQTT_Subscribe(&mqtt, "hermes/nlu/intentNotRecognized"           , MQTT_QOS_1);
//Adafruit_MQTT_Subscribe endSessionSub           = Adafruit_MQTT_Subscribe(&mqtt, "hermes/dialogueManager/endSession"        , MQTT_QOS_1);
//Adafruit_MQTT_Subscribe sessionEndedSub         = Adafruit_MQTT_Subscribe(&mqtt, "hermes/dialogueManager/sessionEnded"      , MQTT_QOS_1);
Adafruit_MQTT_Subscribe toggleOnSub             = Adafruit_MQTT_Subscribe(&mqtt, "hermes/hotword/toggleOn"                  , MQTT_QOS_1);
Adafruit_MQTT_Subscribe olive9000Sub            = Adafruit_MQTT_Subscribe(&mqtt, "hermes/intent/TRosenbaum:openPodBayDoors" , MQTT_QOS_1);

/******************************************************************************
* Bug workaround for Arduino 1.6.6, it seems to need a function declaration   *
* for some reason (only affects ESP8266, likely an arduino-builder bug).      *
******************************************************************************/
void MQTT_connect();
/*****************************************************************************/

// Iris Position Detection
int inputPin = A0;
int pos = 0;

// IRIS Position Smoothing (REF: https://www.arduino.cc/en/tutorial/smoothing)
// Define the number of samples to keep track of. The higher the number, the
// more the readings will be smoothed, but the slower the output will respond to
// the input. Using a constant rather than a normal variable lets us use this
// value to determine the size of the readings array.
const int numReadings = 25;
int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average

// Helper method for logging
void setLine(int level, String statusText) {
  // Print to serial
  Serial.println(statusText);
}

// Function to connect and reconnect as necessary to the MQTT server.
void MQTT_connect() {
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }

  uint8_t retries = 20;
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
    setLine(3,"Retry #" + String(21 - retries));
    setLine(4,mqtt.connectErrorString(ret));
    mqtt.disconnect();
    delay(1000);
    retries--;
    if (retries == 0) {
      setLine(5,"MQTT failed! Reset.");
      // Lock up and wait for Watchdog Timer (WDT) reset
      while (1);
    }
  }

  setLine(5, "MQTT Connected!");
}

void animateBoot() {
  // Iris Ring up animation
  for (int i=0; i<IRIS_NUMPIXELS; i++) {
    delay(50);
    iris.setPixelColor(i, iris.Color(0, 0, 1));
    iris.show();
  }    

  // Green pupil glow
  pupil.setPixelColor(0, pupil.Color(124, 252, 0));
  pupil.show();
}

void animateListening() {
  // Open Iris
  digitalWrite(AIN1, LOW);
  digitalWrite(AIN2, HIGH);
  
  // Ring yellow
  for (int i=0; i<IRIS_NUMPIXELS; i++) {
    delay(10);
    iris.setPixelColor(i, iris.Color(5, 5, 0));
    iris.show();
  }   

  // Turn off Iris
  digitalWrite(AIN1, LOW);
  digitalWrite(AIN2, LOW);
}

void animateEnded() {
  // Open Iris
  digitalWrite(AIN1, LOW);
  digitalWrite(AIN2, HIGH);
  
  // Ring green
  for (int i=0; i<IRIS_NUMPIXELS; i++) {
    delay(10);
    iris.setPixelColor(i, iris.Color(0, 1, 0));
    iris.show();
  }   

  // Turn off Iris
  digitalWrite(AIN1, LOW);
  digitalWrite(AIN2, LOW);
}

void animateIdle() {
  // Close Iris
  digitalWrite(AIN1, HIGH);
  digitalWrite(AIN2, LOW);
  
  // Ring blue
  for (int i=0; i<IRIS_NUMPIXELS; i++) {
    delay(10);
    iris.setPixelColor(i, iris.Color(0, 0, 1));
    iris.show();
  }   

  // Turn off Iris
  digitalWrite(AIN1, LOW);
  digitalWrite(AIN2, LOW);
}

void toggleOff            (char *data, uint16_t len) { setLine(5, "toggleOff          : " + String(data)); animateListening(); }          
void sessionStarted       (char *data, uint16_t len) { setLine(5, "sessionStarted     : " + String(data)); animateListening(); }
void textCaptured         (char *data, uint16_t len) { setLine(5, "textCaptured       : " + String(data)); }
void intentParsed         (char *data, uint16_t len) { setLine(5, "intentParsed       : " + String(data)); }
void intentNotRecognized  (char *data, uint16_t len) { setLine(5, "intentNotRecognized: " + String(data)); }
void endSession           (char *data, uint16_t len) { setLine(5, "endSession         : " + String(data)); animateEnded(); }
void sessionEnded         (char *data, uint16_t len) { setLine(5, "sessionEnded       : " + String(data)); animateIdle(); }
void toggleOn             (char *data, uint16_t len) { setLine(5, "toggleOn           : " + String(data)); animateIdle(); }
void olive9000            (char *data, uint16_t len) { setLine(5, "olive9000          : " + String(data)); animateBoot(); }

// Initialize system
void setup() {
  // Set motor controller to off
  pinMode(AIN1, OUTPUT);
  digitalWrite(AIN1, LOW);
  pinMode(AIN2, OUTPUT);
  digitalWrite(AIN2, LOW);

  // Set up analog reader for Iris position potentiometer
  pinMode(A0, INPUT);
  
  // initialize all the readings to 0:
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }

  // Begin serial
  Serial.begin(9600);
  Serial.println("Booting");

  // Set iris pixel to off
  pupil.begin();
  pupil.show();

  // Set pupil to red
  pupil.setPixelColor(0, pupil.Color(128, 0, 0));
  pupil.show();

  // Dilate for effect
  digitalWrite(AIN1, HIGH);
  digitalWrite(AIN2, LOW);
  delay(500);
  digitalWrite(AIN1, LOW);
  digitalWrite(AIN2, LOW);

  // Init iris ring to off
  iris.begin();  
  iris.show(); // Initialize all ring to 'off'
  
  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;
  //reset settings - for testing
  //wifiManager.resetSettings();

  //sets timeout until configuration portal gets turned off
  //useful to make it all retry or go to sleep
  //in seconds
  wifiManager.setTimeout(300);
  
  //fetches ssid and pass and tries to connect
  //if it does not connect it starts an access point with the specified name
  //here  "AutoConnectAP"
  //and goes into a blocking loop awaiting configuration
  if(!wifiManager.autoConnect()) {
    Serial.println("Could not connect, Timeout while setting up WiFi. Resetting.");
    //reset and try again, or maybe put it to deep sleep
    ESP.reset();
    delay(5000);
  } 
  
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  setLine(2, "Init MQTT...");

  // Setup MQTT Callbacks
  toggleOffSub          .setCallback(toggleOff            );
  //sessionStartedSub     .setCallback(sessionStarted       );
  //textCapturedSub       .setCallback(textCaptured         );
  //intentParsedSub       .setCallback(intentParsed         );
  //intentNotRecognizedSub.setCallback(intentNotRecognized  );
  //endSessionSub         .setCallback(endSession           );
  //sessionEndedSub       .setCallback(sessionEnded         );
  toggleOnSub           .setCallback(toggleOn             );
  //olive9000Sub          .setCallback(olive9000            );

  // Subscribe to topics
  mqtt.subscribe(&toggleOffSub              );
  //mqtt.subscribe(&sessionStartedSub         );
  //mqtt.subscribe(&textCapturedSub           );
  //mqtt.subscribe(&intentParsedSub           );
  //mqtt.subscribe(&intentNotRecognizedSub    );
  //mqtt.subscribe(&endSessionSub             );
  //mqtt.subscribe(&sessionEndedSub           );
  mqtt.subscribe(&toggleOnSub               );
  //mqtt.subscribe(&olive9000Sub              );

  // Connect to MQTT Broker
  MQTT_connect();
  
  animateBoot();
  
  setLine(5,"Boot complete...");
}

void loop() {  
  // subtract the last reading:
  total = total - readings[readIndex];
  // read from the sensor:
  readings[readIndex] = analogRead(inputPin);
  // add the reading to the total:
  total = total + readings[readIndex];
  // advance to the next position in the array:
  readIndex = readIndex + 1;

  // if we're at the end of the array...
  if (readIndex >= numReadings) {
    // ...wrap around to the beginning:
    readIndex = 0;
  }

  // calculate the average:
  pos = total / numReadings;
  //Serial.println(pos);

  // 1% of the time, adjust toward idle position+
  if (random(0,101) <= 1) {
    if (pos >= 27) {
      // CLOSE
      digitalWrite(AIN1,HIGH);
      digitalWrite(AIN2,LOW);
      delay(200);
      digitalWrite(AIN1,LOW);
      digitalWrite(AIN2,LOW);
    }
    else if (pos <= 18) {
      // OPEN
      digitalWrite(AIN1,LOW);
      digitalWrite(AIN2,HIGH);
      delay(100);
      digitalWrite(AIN1,LOW);
      digitalWrite(AIN2,LOW);
    }
  }

  //setLine(0,String(pos));
  // Maintain MQTT Connection
  MQTT_connect();

  // Check for events on subscribed MQTT Topics
  mqtt.processPackets(10);
}

Olive Eye Controller (Servo Version)

C/C++
Microcontroller code for the second Olive prototype, "Olive Mini". This version uses a servo to control the iris diaphragm.
/*
 * Olive - Eye Controller (Servo Version)
 * Author: Travis Rosenbaum (TRosenbaum@gmail.com)
 */

// D-Duino Pinouts
#define D0 16
#define D1 5
#define D2 4
#define D3 0
#define D4 2
#define D5 14
#define D6 12
#define D7 13
#define D8 15
#define TX 1
#define RX 3
#define SDA_PIN D7
#define SCL_PIN D6
 
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager
#include <WiFiUdp.h>

// Servo for iris
#include <Servo.h>
#define IRIS_SERVO_PIN D1
Servo irisServo;

// NeoPixel (WS2812B)
#include <Adafruit_NeoPixel.h>
// Iris LED Setup
#define IRIS_NUMPIXELS 12 // Number of pixels in the Iris ring
#define IRIS_PIXEL_PIN D8
Adafruit_NeoPixel iris = Adafruit_NeoPixel(IRIS_NUMPIXELS + 1, IRIS_PIXEL_PIN, NEO_GRB + NEO_KHZ800);

// Pupil LED Setup (if daisychained, pupil will be in the first slot of the ring above)
//#define PUPIL_PIXEL_PIN D8
//Adafruit_NeoPixel pupil = Adafruit_NeoPixel(1, PUPIL_PIXEL_PIN, NEO_GRB + NEO_KHZ800);

// MQTT Client
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"

// MQTT Client
#define MQTT_SERVER      "192.168.1.77"
#define MQTT_SERVERPORT  1883 // 1883 for MQTT, 8883 for MQTTS
#define MQTT_CLIENTID    "olive-eye-2"
#define MQTT_USERNAME    "olive-eye-2"
#define MQTT_KEY         "key"
//
//// Use WiFiClientSecure for SSL/TLS support
WiFiClient client;
Adafruit_MQTT_Client mqtt(&client, MQTT_SERVER, MQTT_SERVERPORT, MQTT_USERNAME, MQTT_KEY);

/* Hermes - relevant topics:
 *  hermes/hotword/toggleOff                - Hotword detection off (usually when hotword detected, but also when disabled)
 *  hermes/dialogueManager/sessionStarted   - Conversation / listening begins
 *  hermes/asr/textCaptured                 - Text was captured from speech
 *  hermes/nlu/intentParsed                 - The intent was understood, now we wait for the activity
 *  hermes/nlu/intentNotRecognized          - "Man, ain't nobody understand the words that are coming out of your mouth."
 *  hermes/dialogueManager/endSession       - (Optional) The session-ending activity / response.
 *  hermes/dialogueManager/sessionEnded     - The definitive end of the conversation.
 *  hermes/hotword/toggleOn                 - Back to listening for hotword
 *  ----------------------------------------------------------------------------------------------
 *  Other topics:
 *  hermes/intent/TRosenbaum:openPodBayDoors - Olive 9000 Mode
 */
 
Adafruit_MQTT_Subscribe toggleOffSub            = Adafruit_MQTT_Subscribe(&mqtt, "hermes/hotword/toggleOff"                 , MQTT_QOS_1);
//Adafruit_MQTT_Subscribe sessionStartedSub       = Adafruit_MQTT_Subscribe(&mqtt, "hermes/dialogueManager/sessionStarted"    , MQTT_QOS_1);
Adafruit_MQTT_Subscribe textCapturedSub         = Adafruit_MQTT_Subscribe(&mqtt, "hermes/asr/textCaptured"                  , MQTT_QOS_1);
//Adafruit_MQTT_Subscribe intentParsedSub         = Adafruit_MQTT_Subscribe(&mqtt, "hermes/nlu/intentParsed"                  , MQTT_QOS_1);
//Adafruit_MQTT_Subscribe intentNotRecognizedSub  = Adafruit_MQTT_Subscribe(&mqtt, "hermes/nlu/intentNotRecognized"           , MQTT_QOS_1);
//Adafruit_MQTT_Subscribe endSessionSub           = Adafruit_MQTT_Subscribe(&mqtt, "hermes/dialogueManager/endSession"        , MQTT_QOS_1);
//Adafruit_MQTT_Subscribe sessionEndedSub         = Adafruit_MQTT_Subscribe(&mqtt, "hermes/dialogueManager/sessionEnded"      , MQTT_QOS_1);
Adafruit_MQTT_Subscribe toggleOnSub             = Adafruit_MQTT_Subscribe(&mqtt, "hermes/hotword/toggleOn"                  , MQTT_QOS_1);
Adafruit_MQTT_Subscribe olive9000Sub            = Adafruit_MQTT_Subscribe(&mqtt, "hermes/intent/TRosenbaum:openPodBayDoors" , MQTT_QOS_1);

/******************************************************************************
* Bug workaround for Arduino 1.6.6, it seems to need a function declaration   *
* for some reason (only affects ESP8266, likely an arduino-builder bug).      *
******************************************************************************/
void MQTT_connect();
/*****************************************************************************/

// Iris Position Detection
int pos = 0;

// Helper method for logging
void setLine(int level, String statusText) {
  // Print to serial
  Serial.println(statusText);
}

// Function to connect and reconnect as necessary to the MQTT server.
void MQTT_connect() {
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }

  uint8_t retries = 20;
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
    setLine(3,"Retry #" + String(21 - retries));
    setLine(4,mqtt.connectErrorString(ret));
    mqtt.disconnect();
    delay(1000);
    retries--;
    if (retries == 0) {
      setLine(5,"MQTT failed! Reset.");
      // Lock up and wait for Watchdog Timer (WDT) reset
      while (1);
    }
  }

  setLine(5, "MQTT Connected!");
}

void closeIris() {
  irisServo.write(0);
  delay(50);
  irisServo.write(5);
}

void openIris() {
  irisServo.write(90);
  delay(50);
  irisServo.write(85);
}

void animateBoot() {
  
  // Iris Ring up animation
  for (int i=1; i<=IRIS_NUMPIXELS; i++) {
    delay(50);
    iris.setPixelColor(i, iris.Color(0, 0, 16));
    iris.show();
  }    
  
  // Green pupil glow
  iris.setPixelColor(0, iris.Color(16, 32, 0));
  iris.show();

  // Open Iris
  openIris();
}

void animateListening() {
  // Open Iris
  openIris();
  
  // Ring yellow
  for (int i=1; i<=IRIS_NUMPIXELS; i++) {
    delay(10);
    iris.setPixelColor(i, iris.Color(5, 5, 0));
    iris.show();
  }   
}

void animateEnded() {
  // Close Iris
  closeIris();
  
  // Ring green
  for (int i=1; i<=IRIS_NUMPIXELS; i++) {
    delay(10);
    iris.setPixelColor(i, iris.Color(0, 1, 0));
    iris.show();
  }   
}

void animateIdle() {
  // Close Iris
  closeIris();
  
  // Ring blue
  for (int i=1; i<=IRIS_NUMPIXELS; i++) {
    delay(10);
    iris.setPixelColor(i, iris.Color(0, 0, 1));
    iris.show();
  }   
}

void toggleOff            (char *data, uint16_t len) { setLine(5, "toggleOff          : " + String(data)); animateListening(); }          
void sessionStarted       (char *data, uint16_t len) { setLine(5, "sessionStarted     : " + String(data)); animateListening(); }
void textCaptured         (char *data, uint16_t len) { setLine(5, "textCaptured       : " + String(data)); }
void intentParsed         (char *data, uint16_t len) { setLine(5, "intentParsed       : " + String(data)); }
void intentNotRecognized  (char *data, uint16_t len) { setLine(5, "intentNotRecognized: " + String(data)); }
void endSession           (char *data, uint16_t len) { setLine(5, "endSession         : " + String(data)); animateEnded(); }
void sessionEnded         (char *data, uint16_t len) { setLine(5, "sessionEnded       : " + String(data)); animateIdle(); }
void toggleOn             (char *data, uint16_t len) { setLine(5, "toggleOn           : " + String(data)); animateIdle(); }
void olive9000            (char *data, uint16_t len) { setLine(5, "olive9000          : " + String(data)); animateBoot(); }

// Initialize system
void setup() {
  // Setup iris servo
  irisServo.attach(IRIS_SERVO_PIN);

  // Begin serial
  Serial.begin(9600);
  Serial.println("Booting");

  // Init iris ring to off
  iris.begin();  
  iris.show(); // Initialize all LEDs to 'off'

  // Set pupil to red
  iris.setPixelColor(0, iris.Color(16, 0, 0));
  iris.show();

  // Close iris for effect
  closeIris();
  
  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;
  //reset settings - for testing
  //wifiManager.resetSettings();

  //sets timeout until configuration portal gets turned off
  //useful to make it all retry or go to sleep
  //in seconds
  wifiManager.setTimeout(300);
  
  //fetches ssid and pass and tries to connect
  //if it does not connect it starts an access point with the specified name
  //here  "AutoConnectAP"
  //and goes into a blocking loop awaiting configuration
  if(!wifiManager.autoConnect()) {
    Serial.println("Could not connect, Timeout while setting up WiFi. Resetting.");
    //reset and try again, or maybe put it to deep sleep
    ESP.reset();
    delay(5000);
  } 
  
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  setLine(2, "Init MQTT...");

  // Setup MQTT Callbacks
  toggleOffSub          .setCallback(toggleOff            );
  //sessionStartedSub     .setCallback(sessionStarted       );
  //textCapturedSub       .setCallback(textCaptured         );
  //intentParsedSub       .setCallback(intentParsed         );
  //intentNotRecognizedSub.setCallback(intentNotRecognized  );
  //endSessionSub         .setCallback(endSession           );
  //sessionEndedSub       .setCallback(sessionEnded         );
  toggleOnSub           .setCallback(toggleOn             );
  //olive9000Sub          .setCallback(olive9000            );

  // Subscribe to topics
  mqtt.subscribe(&toggleOffSub              );
  //mqtt.subscribe(&sessionStartedSub         );
  //mqtt.subscribe(&textCapturedSub           );
  //mqtt.subscribe(&intentParsedSub           );
  //mqtt.subscribe(&intentNotRecognizedSub    );
  //mqtt.subscribe(&endSessionSub             );
  //mqtt.subscribe(&sessionEndedSub           );
  mqtt.subscribe(&toggleOnSub               );
  //mqtt.subscribe(&olive9000Sub              );

  // Connect to MQTT Broker
  MQTT_connect();
  
  animateBoot();
  
  setLine(5,"Boot complete...");
}

void loop() {  

  //setLine(0,String(pos));
  // Maintain MQTT Connection
  MQTT_connect();

  // Check for events on subscribed MQTT Topics
  mqtt.processPackets(10);
}

Credits

Travis Rosenbaum

Travis Rosenbaum

1 project • 8 followers

Comments