Fauxmo ESP8266 + millis() Air freshener project

Hi, I have a ESP8266 and Fauxmo esp library. I have it set up and it connects to wifi no problem, voice commands work, but the issue I cant figure out is how to turn the pin back off after 10 seconds. I tired delay in the bool statement and it works but Alexa always says device is malfunctioning. I can imagine it saying that because it did not get a return true statement for turning the light on.

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiUdp.h>
#include <functional>
#include "switch.h"
#include "UpnpBroadcastResponder.h"
#include "CallbackFunction.h"

unsigned long currentMillis;
unsigned long previousMillis = 0;       // will store last time LED was updated
const long interval = 10000;            // 10 seconds interval

int ledState;

boolean connectWifi();

//on/off callbacks
bool lightOn();            // BUILTIN_LED 16 by usb port
bool lightOff();           // BUILTIN_LED 16 by usb port

bool airfreshenerOn();     // BUILTIN_LED 2 by antenna
bool airfreshenerOff();    // BUILTIN_LED 2 by antenna

// Change this before you flash
//#######################################
const char* ssid = "wifiname";            // enter your access point/wifi router name
const char* password = "wifipassword";     // enter router password
//#######################################

boolean wifiConnected = false;

UpnpBroadcastResponder upnpBroadcastResponder;

Switch *light = NULL;
Switch *airfreshener = NULL;
bool islightsOn = true;
bool isAirfreshenerOn = true;

void setup()
{
  Serial.begin(115200);
  pinMode(2, OUTPUT);
  pinMode(16, OUTPUT);
  digitalWrite(2, HIGH);          //turn BUILTIN_LED off, not sure why HIGH turns it off
  digitalWrite(16, HIGH);         //turn BUILTIN_LED off, not sure why HIGH turns it off


  // Initialise wifi connection
  wifiConnected = connectWifi();

  if (wifiConnected) {
    upnpBroadcastResponder.beginUdpMulticast();

    // Define your switches here. Max 14
    // Format: Alexa invocation name, local port no, on callback, off callback
    light = new Switch("light", 80, lightOn, lightOff);
    airfreshener = new Switch("air freshener", 81, airfreshenerOn, airfreshenerOff);

    Serial.println("Adding switches upnp broadcast responder");
    upnpBroadcastResponder.addDevice(*light);
    upnpBroadcastResponder.addDevice(*airfreshener);
  }
}

void loop()
{
  if (wifiConnected) {
    upnpBroadcastResponder.serverLoop();

    airfreshener->serverLoop();
    light->serverLoop();
    }

    unsigned long currentMillis = millis();

    if (currentMillis - previousMillis >= interval)   // inconsistant timing by seconds: 3 - 20 seconds
    {
      previousMillis = currentMillis;                 // save the last time event took place

      if (ledState == LOW)                            // if the LED is ON (LOW)  turn it OFF (HIGH)
      {
        ledState = HIGH;
      }
      digitalWrite(2, ledState);                      // set the LED with the ledState of the variable:
    }

}

bool lightOn() {
  Serial.print("Switch 1 turn on ...");
  digitalWrite(16, LOW);              // Builtin_LED_1
  return true;
}

bool lightOff() {
  Serial.print("Switch 1 turn off ...");
  digitalWrite(16, HIGH);             // Builtin_LED_1
  return false;
}

bool airfreshenerOn() {
  Serial.print("Switch 2 turn on ...");
  digitalWrite(2, LOW);               // Turns ON Builtin_LED_2
  //     return true;                 // Shows ON in app
  // }

  //  delay(10000);                   // Often causes Alexa to say "sorry Airfreshner appears to be malfunctioning" but works
  //  digitalWrite(2, HIGH);          // Turns OFF OFF Builtin_LED_2
  //  return false;                   // Shows OFF in app
  return true;                        // Shows ON in app
}


bool airfreshenerOff() {
  Serial.print("Switch 2 turn off ...");
  digitalWrite(2, HIGH);
  return false;
}




// connect to wifi – returns true if successful or false if not
boolean connectWifi() {
  boolean state = true;
  int i = 0;

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");
  Serial.println("Connecting to WiFi");

  // Wait for connection
  Serial.print("Connecting ...");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if (i > 10) {
      state = false;
      break;
    }
    i++;
  }

  if (state) {
    Serial.println("");
    Serial.print("Connected to ");
    Serial.println(ssid);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  }
  else {
    Serial.println("");
    Serial.println("Connection failed.");
  }

  return state;
}

with the millis() code in the void loop and the command to turn on airfreshner, the time the builtin led is on... ranges from a quick flash, to 7 seconds. 10 Seconds is good enough to power the airfreshner, let it go through its startup routine and it will dispense 1 squirt of fragrance and then be disconnected from power when pin2 changes to high.

Any advice on what I need to fix to get pin2 (in this case) to turn off after 10 seconds.

Thanks,
Clint

Can you describe or show how your program is working now? You have the "timing" commented-out in the "air fresheneron" function... but "true" and "false" are in that function. If the timer is greater than 10000ms, you want to return "false"?

Hi, I have it commented out since the delay in the bool statement makes Alexa say the device is malfunctioning and tired to use millis in the void loop but the timing is inconsistent, like I am missing how to correctly start the timer or something.
let met update the code here with no comment out lines.

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiUdp.h>
#include <functional>
#include "switch.h"
#include "UpnpBroadcastResponder.h"
#include "CallbackFunction.h"

unsigned long currentMillis;
unsigned long previousMillis = 0;       // will store last time LED was updated
const long interval = 10000;            // 10 seconds interval

int ledState;

boolean connectWifi();

//on/off callbacks
bool lightOn();            // BUILTIN_LED 16 by usb port
bool lightOff();           // BUILTIN_LED 16 by usb port

bool airfreshenerOn();     // BUILTIN_LED 2 by antenna
bool airfreshenerOff();    // BUILTIN_LED 2 by antenna

// Change this before you flash
//#######################################
const char* ssid = "SSID";            // enter your access point/wifi router name
const char* password = "PASS!";     // enter router password
//#######################################

boolean wifiConnected = false;

UpnpBroadcastResponder upnpBroadcastResponder;

Switch *light = NULL;
Switch *airfreshener = NULL;
bool islightsOn = true;
bool isAirfreshenerOn = true;

void setup()
{
  Serial.begin(115200);
  pinMode(2, OUTPUT);
  pinMode(16, OUTPUT);
  digitalWrite(2, HIGH);          //turn BUILTIN_LED off, not sure why HIGH = off
  digitalWrite(16, HIGH);         //turn BUILTIN_LED off, not sure why HIGH = off


  // Initialise wifi connection
  wifiConnected = connectWifi();

  if (wifiConnected) {
    upnpBroadcastResponder.beginUdpMulticast();

    // Define your switches here. Max 14
    // Format: Alexa invocation name, local port no, on callback, off callback
    light = new Switch("light", 80, lightOn, lightOff);
    airfreshener = new Switch("air freshener", 81, airfreshenerOn, airfreshenerOff);

    Serial.println("Adding switches upnp broadcast responder");
    upnpBroadcastResponder.addDevice(*light);
    upnpBroadcastResponder.addDevice(*airfreshener);
  }
}

void loop()
{
  if (wifiConnected) {
    upnpBroadcastResponder.serverLoop();

    airfreshener->serverLoop();
    light->serverLoop();
  }

  if (ledState == LOW)
  {
    unsigned long currentMillis = millis();

    if (currentMillis - previousMillis >= interval)   // inconsistant timing by seconds: 3 - 20 seconds
    {
      previousMillis = currentMillis;                 // save the last time event took place

      //      if (ledState == LOW)                            // if the LED is ON (LOW)  turn it OFF (HIGH)
      //    {
      ledState = HIGH;
      //}
      digitalWrite(2, ledState);                      // set the LED with the ledState of the variable:
    }
  }
}

bool lightOn() {
  Serial.print("Switch 1 turn on ...");
  digitalWrite(16, LOW);              // Builtin_LED_1
  return true;
}

bool lightOff() {
  Serial.print("Switch 1 turn off ...");
  digitalWrite(16, HIGH);             // Builtin_LED_1
  return false;
}




bool airfreshenerOn() {
  Serial.print("Switch 2 turn on ...");
  digitalWrite(2, LOW);               // Turns ON Builtin_LED_2
  ledState = LOW;
  digitalWrite(2, ledState);
  return true;                        // Shows ON in app
}




bool airfreshenerOff() {
  Serial.print("Switch 2 turn off ...");
  digitalWrite(2, HIGH);
  return false;
}




// connect to wifi – returns true if successful or false if not
boolean connectWifi() {
  boolean state = true;
  int i = 0;

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");
  Serial.println("Connecting to WiFi");

  // Wait for connection
  Serial.print("Connecting ...");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if (i > 10) {
      state = false;
      break;
    }
    i++;
  }

  if (state) {
    Serial.println("");
    Serial.print("Connected to ");
    Serial.println(ssid);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  }
  else {
    Serial.println("");
    Serial.println("Connection failed.");
  }

  return state;
}

This code when I say "turn on airfreshner: the light blinks for maybe .5 second and then off

Probably the 10000ms of delay() makes Alexa think the thing is broke... I will check your previous post... (I see you writing).

Is your desired result the light blinks for ten seconds then stops blinking?

Note: Is this LED wired with cathode set high, then to turn it on the cathode pin is set low?

  digitalWrite(2, LOW);               // Turns ON Builtin_LED_2
  ledState = LOW;

Desired result would be for the LED to turn ON for 10 seconds, and then Turn OFF

I'm making changes to:

void loop()
and
bool airfreshenerOn()

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiUdp.h>
#include <functional>
#include "switch.h"
#include "UpnpBroadcastResponder.h"
#include "CallbackFunction.h"

unsigned long currentMillis;
unsigned long previousMillis = 0;       // will store last time LED was updated
const long interval = 10000;            // 10 seconds interval

int ledState;

boolean connectWifi();

//on/off callbacks
bool lightOn();            // BUILTIN_LED 16 by usb port
bool lightOff();           // BUILTIN_LED 16 by usb port

bool airfreshenerOn();     // BUILTIN_LED 2 by antenna
bool airfreshenerOff();    // BUILTIN_LED 2 by antenna

// Change this before you flash
//#######################################
const char* ssid = "SSID";            // enter your access point/wifi router name
const char* password = "PASS!";     // enter router password
//#######################################

boolean wifiConnected = false;

UpnpBroadcastResponder upnpBroadcastResponder;

Switch *light = NULL;
Switch *airfreshener = NULL;
bool islightsOn = true;
bool isAirfreshenerOn = true;

void setup()
{
  Serial.begin(115200);
  pinMode(2, OUTPUT);
  pinMode(16, OUTPUT);
  digitalWrite(2, HIGH);          //turn BUILTIN_LED off, not sure why HIGH = off
  digitalWrite(16, HIGH);         //turn BUILTIN_LED off, not sure why HIGH = off


  // Initialise wifi connection
  wifiConnected = connectWifi();

  if (wifiConnected) {
    upnpBroadcastResponder.beginUdpMulticast();

    // Define your switches here. Max 14
    // Format: Alexa invocation name, local port no, on callback, off callback
    light = new Switch("light", 80, lightOn, lightOff);
    airfreshener = new Switch("air freshener", 81, airfreshenerOn, airfreshenerOff);

    Serial.println("Adding switches upnp broadcast responder");
    upnpBroadcastResponder.addDevice(*light);
    upnpBroadcastResponder.addDevice(*airfreshener);
  }
}

void loop()
{
  if (wifiConnected) {
    upnpBroadcastResponder.serverLoop();

    airfreshener->serverLoop();
    light->serverLoop();
  }

  if (ledState == LOW)
  {
    unsigned long currentMillis = millis();
    
    if (currentMillis - previousMillis >= interval)   // inconsistant timing by seconds: 3 - 20 seconds
    {
      previousMillis = currentMillis;                 // save the last time event took place

      ledState = HIGH;                                // HIGH = OFF
      digitalWrite(2, ledState);                      // set the LED with the ledState of the variable:
    }
  }
}

bool lightOn() {
  Serial.print("Switch 1 turn on ...");
  digitalWrite(16, LOW);              // Builtin_LED_1
  return true;
}

bool lightOff() {
  Serial.print("Switch 1 turn off ...");
  digitalWrite(16, HIGH);             // Builtin_LED_1
  return false;
}




bool airfreshenerOn() {
  Serial.print("Switch 2 turn on ...");
  ledState = LOW;                     // Turns ON Builtin_LED_2
  digitalWrite(2, ledState);          // Update ledState to ON
  return true;                        // Shows ON in app
}




bool airfreshenerOff() {
  Serial.print("Switch 2 turn off ...");
  digitalWrite(2, HIGH);
  return false;
}




// connect to wifi – returns true if successful or false if not
boolean connectWifi() {
  boolean state = true;
  int i = 0;

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");
  Serial.println("Connecting to WiFi");

  // Wait for connection
  Serial.print("Connecting ...");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if (i > 10) {
      state = false;
      break;
    }
    i++;
  }

  if (state) {
    Serial.println("");
    Serial.print("Connected to ");
    Serial.println(ssid);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  }
  else {
    Serial.println("");
    Serial.println("Connection failed.");
  }

  return state;
}

I will say turn on airfreshner :
the LED turns ON and sometimes LED will turn off quickly, sometimes the LED will stay on for a second or 3, but not 10 seconds (or close)

Thank you @xfpd for taking time to help me by the way :slight_smile:
Clint

edit: i think the bool statement might be ok, I just want the esp8266 turn the light off after 10 seconds.

I think something is causing a call to airfreshnerOff();... what condition calls this?

Or... should this be in airfreshenerOn()?

  ledState = LOW;
  digitalWrite(2, ledState);

lightOn() does not have this.

bool airfreshenerOn() {
  Serial.print("Switch 2 turn on ...");
  ledState = LOW;                     // Turns ON Builtin_LED_2
  digitalWrite(2, ledState);          // Update ledState to ON
  return true;                        // Shows ON in app
}

updates the ledState

the lightOn that is just to test the other led. That works as expected - stays ON or OFF when requested.

Does the millis() function in the loop look normal to you?

If Alexa didnt say device is malfunctioning, I would leave delay in the bool statement :slight_smile:

I am not familiar with Alexa's code, so I wonder... should the "new" be in this function... because it seems to me each time through this function, an new "Switch" will be created...

  if (wifiConnected) {
    upnpBroadcastResponder.beginUdpMulticast();

    // Define your switches here. Max 14
    // Format: Alexa invocation name, local port no, on callback, off callback
    light = new Switch("light", 80, lightOn, lightOff);
    airfreshener = new Switch("air freshener", 81, airfreshenerOn, airfreshenerOff);

    Serial.println("Adding switches upnp broadcast responder");
    upnpBroadcastResponder.addDevice(*light);
    upnpBroadcastResponder.addDevice(*airfreshener);
  }

No. "typing" currentMillis (unsigned long currentMillis) creates a new currentMillis each loop... so

Make this global... (somewhere above setup() )

    unsigned long currentMillis;

Then use it like this...

  if (ledState == LOW)
  {
    currentMillis = millis();
    if (currentMillis - previousMillis >= interval)
    {
      previousMillis = currentMillis;
      ledState = HIGH;
      digitalWrite(2, ledState);
    }
  }
1 Like

By no means am I familiar with it either, I feel it is saying
if wifi is connected, add these devices
light
airfreshner

I change the void loop()

void loop()
{
  if (wifiConnected) {
    upnpBroadcastResponder.serverLoop();

    airfreshener->serverLoop();
    light->serverLoop();
  }
  
/*  if (ledState == LOW)
  {
    unsigned long currentMillis = millis();
    
    if (currentMillis - previousMillis >= interval)   // inconsistant timing by seconds: 3 - 20 seconds
    {
      previousMillis = currentMillis;                 // save the last time event took place

      ledState = HIGH;                                // HIGH = OFF
      digitalWrite(2, ledState);                      // set the LED with the ledState of the variable:
    }
  }
  */
}

this change to the code removes my millis() attempt and the airfreshner (LED) operates ON or OFF as the its commanded to.. but the goal is to have the LED turn off after 10 seconds.
Im gonna youtube some more research

I will try this out and report back :slight_smile:

ew.

jk. : )

haha, I'm a slow learner that tries hard :slight_smile:

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiUdp.h>
#include <functional>
#include "switch.h"
#include "UpnpBroadcastResponder.h"
#include "CallbackFunction.h"

unsigned long currentMillis;
unsigned long previousMillis = 0;       // will store last time LED was updated
const long interval = 10000;            // 10 seconds interval

int ledState;

boolean connectWifi();

//on/off callbacks
bool lightOn();            // BUILTIN_LED 16 by usb port
bool lightOff();           // BUILTIN_LED 16 by usb port

bool airfreshenerOn();     // BUILTIN_LED 2 by antenna
bool airfreshenerOff();    // BUILTIN_LED 2 by antenna

// Change this before you flash
//#######################################
const char* ssid = "SSID";            // enter your access point/wifi router name
const char* password = "PASS!";     // enter router password
//#######################################

boolean wifiConnected = false;

UpnpBroadcastResponder upnpBroadcastResponder;

Switch *light = NULL;
Switch *airfreshener = NULL;
bool islightsOn = true;
bool isAirfreshenerOn = true;

void setup()
{
  Serial.begin(115200);
  pinMode(2, OUTPUT);
  pinMode(16, OUTPUT);
  digitalWrite(2, HIGH);          //turn BUILTIN_LED off, not sure why HIGH = off
  digitalWrite(16, HIGH);         //turn BUILTIN_LED off, not sure why HIGH = off


  // Initialise wifi connection
  wifiConnected = connectWifi();

  if (wifiConnected) {
    upnpBroadcastResponder.beginUdpMulticast();

    // Define your switches here. Max 14
    // Format: Alexa invocation name, local port no, on callback, off callback
    light = new Switch("light", 80, lightOn, lightOff);
    airfreshener = new Switch("air freshener", 81, airfreshenerOn, airfreshenerOff);

    Serial.println("Adding switches upnp broadcast responder");
    upnpBroadcastResponder.addDevice(*light);
    upnpBroadcastResponder.addDevice(*airfreshener);
  }
}

void loop()
{
  if (wifiConnected) {
    upnpBroadcastResponder.serverLoop();

    airfreshener->serverLoop();
    light->serverLoop();
  }
  
  if (ledState == LOW)
  {
    currentMillis = millis();    
    if (currentMillis - previousMillis >= interval)   // inconsistant timing by seconds: 3 - 20 seconds
    {
      previousMillis = currentMillis;                 // save the last time event took place
      ledState = HIGH;                                // HIGH = OFF
      digitalWrite(2, ledState);                      // set the LED with the ledState of the variable:
    }
  }
  
}

bool lightOn() {
  Serial.print("Switch 1 turn on ...");
  digitalWrite(16, LOW);              // Builtin_LED_1
  return true;
}

bool lightOff() {
  Serial.print("Switch 1 turn off ...");
  digitalWrite(16, HIGH);             // Builtin_LED_1
  return false;
}




bool airfreshenerOn() {
  Serial.print("Switch 2 turn on ...");
  ledState = LOW;                     // Turns ON Builtin_LED_2
  digitalWrite(2, ledState);          // Update ledState to ON
  return true;                        // Shows ON in app
}




bool airfreshenerOff() {
  Serial.print("Switch 2 turn off ...");
  digitalWrite(2, HIGH);
  return false;
}




// connect to wifi – returns true if successful or false if not
boolean connectWifi() {
  boolean state = true;
  int i = 0;

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");
  Serial.println("Connecting to WiFi");

  // Wait for connection
  Serial.print("Connecting ...");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if (i > 10) {
      state = false;
      break;
    }
    i++;
  }

  if (state) {
    Serial.println("");
    Serial.print("Connected to ");
    Serial.println(ssid);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  }
  else {
    Serial.println("");
    Serial.println("Connection failed.");
  }

  return state;
}

results are similar to initial results, when airfreshener ON is commanded:
LED might turn ON for a split second and then OFF, or the LED turns ON,OFF super quick and then ON for a second or 2, other times turns ON for a few seconds, but not the 10 second interval. maybe a limitation with the library or Alexa? Ill come back to this after dinner :slight_smile:

Something is changing the state of the LED. (LED is easier to write than airfreshener). In your original/normal program, you tell the LED to turn on and the LED (state) turns on and stays on. In your modified program, you tell the LED to turn on (and it does) but then turns off... something is changing the state... this seems to be the only thing that is referring to a variable "...state..."

bool airfreshenerOn() {
  Serial.print("Switch 2 turn on ...");
  ledState = LOW;                     // Turns ON Builtin_LED_2
  digitalWrite(2, ledState);          // Update ledState to ON
  return true;                        // Shows ON in app
}

Make the above, look like the below, and give it a try... I know they are the same, but I like to check all the boxes.

bool airfreshenerOn() {
  Serial.print("Switch 2 turn on ...");
  digitalWrite(2, LOW);          // Update ledState to ON
  return true;                        // Shows ON in app
}

... AND... does the app say if the LED is ON or OFF?

Check that this is counting to 10 seconds...

  if (ledState == LOW)
  {
    currentMillis = millis();    
    if (currentMillis - previousMillis >= interval)   // inconsistant timing by seconds: 3 - 20 seconds
    {
      previousMillis = currentMillis;                 // save the last time event took place
      ledState = HIGH;                                // HIGH = OFF
      digitalWrite(2, ledState);                      // set the LED with the ledState of the variable:
    }
  }

And checking another box, change this:

const long interval = 10000;            // 10 seconds interval

to this

const unsigned long interval = 10000;            // 10 seconds interval

Just to keep the math from struggling over the right number of bytes.

And one more guess... Amazon developer says not to use modifiers like "the lamp" ... so I wonder if this causes two "on" commands because you have a modifier and a noun (air and freshener)... maybe call it "fresh"...

    airfreshener = new Switch("air freshener", 81, airfreshenerOn, airfreshenerOff);

I do not understand Amazon developer not wanting modifiers, because when I say "lamp off" Alexa throws a fit asking, "You mean the lamp by the front door?" Eesh.

p.s. My previous question about "new" in "new Switch"... your code is correct.

... stop the press...

I made another mistake on the 10-seconds timer.

I was setting previousMillis inside the timer... so previousMillis was "zero" for the first condition and currentMillis was probably getting close to 10000ms by the time you gave the ON command... so soon after the airfreshener/LED was turned on, the math (currentMillis - previousMillis > interval) was true (time > 10s) and the airfreshener/LED was turned off.

What I (think) should happen is setting previousMillis to millis() INSIDE the airfreshenerOn() function... that will give previousMillis a real value (greater than zero) at the time the command was given... for when the code returns to the timer in loop()... try...

  if (ledState == LOW)
  {
    if (millis() - previousMillis >= interval)
    {
      digitalWrite(2, HIGH);
    }
  }

and

bool airfreshenerOn() {
  Serial.print("Switch 2 turn on ...");
  digitalWrite(2, LOW);
  previousMillis = millis();  // time when command was given
  return true;
}
1 Like

Use a millis() loop to delay 10 secs then set the pin low.

LOW is ON in this machine (HIGH is OFF).