Igor Fonseca Albuquerque
Published © CC BY-NC-SA

IoT Air Freshener (w/ NodeMCU, Arduino, Adafruit.io, IFTTT)

An IoT Air Freshener, using some 3D printing, a NodeMCU, Arduino IDE, IFTTT and Adafruit.IO. Configure your own triggers and have fun!

IntermediateFull instructions provided10 hours4,572

Things used in this project

Hardware components

NodeMCU ESP8266 Breakout Board
NodeMCU ESP8266 Breakout Board
×1
NeoPixel Ring: WS2812 5050 RGB LED
Adafruit NeoPixel Ring: WS2812 5050 RGB LED
×1
mg995 servo
×1

Software apps and online services

Arduino IDE
Arduino IDE
Maker service
IFTTT Maker service
Adafruit.io
Fusion 360
Autodesk Fusion 360

Hand tools and fabrication machines

Anet A8 3d printer

Story

Read more

Custom parts and enclosures

Thingiverse

https://www.thingiverse.com/thing:2613327

Schematics

Schematics

Schematics - breadboard

Code

Arduino Code

Arduino
// IoT Air Freshener code
// Commands are collected in a feed
// Multiple NeoPixels visualize the command
// Servo actuates output
//
// Modified by Igor Fonseca Albuquerque 2017
// based on Instructables IoT Class by Becky Stern 2017
// based on the Adafruit IO Subscription 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 Todd Treece for Adafruit Industries
// Copyright (c) 2016 Adafruit Industries
// Licensed under the MIT license.
//
// All text above must be included in any redistribution.

/************************ Adafruit IO Configuration *******************************/

// visit io.adafruit.com if you need to create an account,
// or if you need your Adafruit IO key.
#define IO_USERNAME    "XXXXXXXXXX"
#define IO_KEY         "YYYYYYYYY"

/******************************* WIFI Configuration **************************************/

#define WIFI_SSID       "WWWWWWWWWW"
#define WIFI_PASS       "ZZZZZZZZZZ"

#include "AdafruitIO_WiFi.h"
AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS);

/************************ Main Program Starts Here *******************************/
#include <ESP8266WiFi.h>
#include <AdafruitIO.h>
#include <Adafruit_MQTT.h>
#include <ArduinoHttpClient.h>
#include "Servo.h"
#include <Adafruit_NeoPixel.h>

#define SERV1 12 // Pin connected to the Servomotor
Servo s1;

#define BUTTON_PIN 14 // Pin connected to the pushbutton

#define PIXELS_PIN 15   // Pin connected to the NeoPixel data input
#define NUM_LEDS   16   // Number of NeoPixels
#define BRIGHTNESS 30
#define PIXEL_TYPE NEO_GRB + NEO_KHZ800   // Type of the NeoPixels (see strandtest example).

Adafruit_NeoPixel ring = Adafruit_NeoPixel(NUM_LEDS, PIXELS_PIN, PIXEL_TYPE);// + NEO_KHZ800);

AdafruitIO_Feed *command = io.feed("iot-air-freshener-command"); // set up the 'command' feed

void setup() {

  ring.setBrightness(BRIGHTNESS);
  ring.begin();
  ring.show(); // Initialize all pixels to 'off'

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

  // connect to io.adafruit.com
  Serial.print("Connecting to Adafruit IO");
  io.connect();
  
  // set up a message handler for the 'command' feed.
  // the handleMessage function (defined below)
  // will be called whenever a message is
  // received from adafruit io.
  command->onMessage(handleMessage);  

  // wait for a connection
  int i = NUM_LEDS - 1;
  int color = 255;
  // animate LEDs while waiting for connection
  while(io.status() < AIO_CONNECTED) {
    Serial.print(".");
    ring.setPixelColor(i, 0, 0, color);
    ring.show();
    i = i - 1;
    if (i < 0) {
      if (color == 255) {
        color = 0;
      }
      else {
        color = 255;
      }
      i = NUM_LEDS - 1;
    }
    delay(50);
  }
  lightPixels(ring.Color(0, 0, 0, 0)); // reset all pixels to off when connected

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

  // move servomotor to neutral position
  s1.attach(SERV1);
  s1.write(90);
  delay(400);
  s1.detach();

  // set button pin as an input
  pinMode(BUTTON_PIN, INPUT_PULLUP);

}

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();

  if(digitalRead(BUTTON_PIN) == LOW) {
    command->save("button");
  }

}

// this function is called whenever a message
// is received from Adafruit IO. it was attached to
// the feed in the setup() function above.
void handleMessage(AdafruitIO_Data *data) {
  
  lightPixels(ring.Color(0, 0, 0, 0)); // reset all pixels to off when new info is received

  String commandStr = data->toString(); // store the incoming commands in a string
  
  Serial.print("received <- ");
  Serial.println(commandStr);
  
  // These if statements compare the incoming weather variable to the stored conditions, and control the NeoPixels accordingly.
  
  // if virtual push button was pressed
  if (commandStr.equalsIgnoreCase("button")){
    Serial.println("Virtual push button");
    rotatingPixels(ring.Color(255, 255, 0, 0)); // display animation
    lightPixels(ring.Color(255, 255, 0, 0)); // display animation
    launch(1);
    lightPixels(ring.Color(0, 0, 0, 0)); // reset all pixels to off when new info is received
  }
  
  // if it's time
  if (commandStr.equalsIgnoreCase("timer")){
    Serial.println("it's time");
    rotatingPixels(ring.Color(0, 0, 255, 0)); // display animation
    lightPixels(ring.Color(0, 0, 255, 0)); // display animation
    launch(2);
    lightPixels(ring.Color(0, 0, 0, 0)); // reset all pixels to off when new info is received
  }
  
  // if location reached
  if (commandStr.equalsIgnoreCase("location")){
    Serial.println("welcome home!");
    rotatingPixels(ring.Color(0, 255, 0, 0)); // display animation
    lightPixels(ring.Color(0, 255, 0, 0)); // display animation
    launch(2);
    lightPixels(ring.Color(0, 0, 0, 0)); // reset all pixels to off when new info is received
  }
  
  // if you've got mail
  if (commandStr.equalsIgnoreCase("mail")){
    Serial.println("you've got mail!");
    rotatingPixels(ring.Color(255, 0, 0, 0)); // display animation
    lightPixels(ring.Color(255, 0, 0, 0)); // display animation
    launch(1);
    lightPixels(ring.Color(0, 0, 0, 0)); // reset all pixels to off when new info is received
   }
  } 


// Function to set all the NeoPixels to the specified color.
void lightPixels(uint32_t color) {
  for (int i=0; i<NUM_LEDS; ++i) {
    ring.setPixelColor(i, color);
  }
  ring.show();
}


//Funcion NeoPixels full rotation
void rotatingPixels(uint32_t color) {
  for (int j = 0; j < 3; j++) {
    for (int i=NUM_LEDS-1; i>=0; i--) {
      ring.setPixelColor(i, color);
      ring.show();
      delay(50);
      ring.setPixelColor(i, 0, 0, 0);//ring.Color(0, 0, 0, 255));
      ring.show();
    }
  }
}

//Actuates the servomotor
void launch (int number) {
  s1.attach(SERV1);
  for (int i = 0; i < number; i++) {    
    s1.write(175);
    delay(1000);
    s1.write(90);
    delay(1000);
  }
  s1.detach();
}

Github

https://github.com/adafruit/Adafruit_NeoPixel

Github

https://github.com/arduino-libraries/ArduinoHttpClient

Github

https://github.com/adafruit/Adafruit_IO_Arduino

Github

https://github.com/adafruit/Adafruit_MQTT_Library

Credits

Igor Fonseca Albuquerque

Igor Fonseca Albuquerque

18 projects • 278 followers
Electrical and Mechanical engineer, master in automation and control. specialist in industrial instrumentation. Mad scientist and inventor.

Comments