ESP8266 Wemos D1 Mini - Time Input Widget

Hey pete,

I’m now at the point of your recommended software interrupt. I’ve never heard of that before and I’m startpaging (no, not googling :D) around the internet for this. Can you just tell me if that is what I have to look for?

//Written 8/10/2015 by Reperio

const byte LED = 13; //LED On Arduino Uno
const byte PIN = 2; //On the Uno this can be either pin 2 or 3
 
// Declare Interrupt Service Routine (ISR)
void pinChange ()
{
  if (digitalRead (PIN) == HIGH)
    digitalWrite (LED, HIGH);
  else
    digitalWrite (LED, LOW);
}  // end of ISR

void setup ()
{
  pinMode (LED, OUTPUT);  // so we can update the LED
  attachInterrupt (0, pinChange, CHANGE);  // attach interrupt handler to look for changes on the pin
}

void loop ()
{
  delay(1500);
  digitalWrite(PIN, LOW);
  delay(1500);
  digitalWrite(PIN, HIGH);
}

//End of program

if found it here => https://forum.arduino.cc/index.php?topic=60668.0

But now I don’t know how I have to set it up correct.

I have 2 PIR voids.

void PIR_check_timer checks if the time is inside the time window. if so the timer for void PIR_on is activated (once every second, so the LED effects can run fluently).

So the softwareinterrupt should be done this way, that if the PIR is HIGH the PIR_check_timer should be run?

The interrupt code you posted is written for a Uno, which only allows interrupts to be attached to a couple of pins. The D1 Mini allows interrupts to be attached to any of the GPIO pins, so has more flexibility and allows you to accommodate more PIR’s on one device.
Interrupts can be declared as RISING, FALLING OR CHANGE. If you PIR goes HIGH when active then you’ll be using RISING.

When the interrupt fires, it will run the function that you’ve declared. Personally, I’d structure the code so that once interrupt fires it checks in the current time is within the PIR operating window and if it is then operate your ‘alarm’ LEDs.

Pete.

I thought I understand how to do it with my attachedinterrupt but it is not running at all.
My PIR is connected to D3 on the ESP. Should I have to change it now because of arduino Pin 2 and Pin 3?

#define BLYNK_PRINT Serial

#include <Adafruit_NeoPixel.h>
#include <BlynkSimpleEsp8266.h>
#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <SPI.h>
#include <WiFiUdp.h>

// LED Stuff
#define PIN D5
#define NUM_LEDS 37
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ400);
void showStrip();
void clearStrip();
void setPixel(int, int, int, int);
void fadeToBlack(int, byte);
void ZeRGBa();
void colorWipe(int, int, int, unsigned long);
void CylonBounce(int, int, int, int, int, int);
void meteorRain(int, int, int, int, int, boolean, unsigned long);
void Strobe(int, int, int, int, int, int);

// normal Void Declaration
void WIFI_check();
void ALARM_time_check();
void ALARM_on();
void PIR_time_check();
void PIR_on();
void WDAY_and_NOW();
void NTP_update();
void PIN_change();
void bdelay(unsigned long);

// WiFi Stuff
**** STUF AS USAL  ****

// Variables

int red = 1;
int green = 0;
int blue = 2;

int connectionSuccess = 0;

// Alarm Variables
int ALARM_start = 21600;
int ALARM_end_time;
int ALARM_timer = 10000;
int ALARM_effect = 4;
int ALARM_counter;
char ALARM_days_start[] = "1,2,3,4,5";
bool ALARM_days[] = {true, true, true, true, true, false, false};

// Motion Sensor Variables
int PIR_pin = D3;
int PIR_val;
int PIR_start = 79200;
int PIR_stop = 21600;
int PIR_end_time;
int PIR_timer = 10 * 1000;
int PIR_effect = 1;
char PIR_days_start[] = "1,2,3,4,5,6,7";
bool PIR_days[] = {true, true, true, true, true, true, true};
bool PIR_active = false;

const long utcOffsetInSeconds = 2 * 60 * 60;

bool debug = true;

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "10.0.4.1", utcOffsetInSeconds);
unsigned long TIME_sync_millis = 0;
int TIME_sync_timer = 60;
int now;
int finish;
int weekday;

int WIFI_check_timerID;
int NTP_update_timerID;
int ALARM_check_timerID;
int ALARM_on_timerID;
int PIR_on_timerID;

unsigned long currentMillis;
unsigned long colorWipeMillis;

BlynkTimer timer;

void setup()
{
  Serial.begin(115200);

  unsigned long started = millis();
  Serial.println();
  WiFi.begin(ssid, pass); //Blynk.begin(auth, ssid, pass, serv, 8080);
  Serial.println(String("Connecting to ") + ssid + " ...");
  while (WiFi.status() != WL_CONNECTED) {
    if (millis() - started < 20 * 1000) {
      yield();
    }
  }
  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("WiFi connected");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    Serial.println("Wifi works, now try Blynk connection");
    delay(2 * 1000);
    Blynk.config(auth, serv, 8080);
    Blynk.connect(30 * 1000);
    if (Blynk.connected() == true) {
      delay(2000);
      Blynk.run();
      Serial.println("Blynk is online");
    }
  }

  timeClient.begin();

  WIFI_check_timerID = timer.setInterval(5 * 1000L, WIFI_check);
  ALARM_check_timerID = timer.setInterval(60 * 1000L, ALARM_time_check);
  ALARM_on_timerID = timer.setInterval(1 * 1000L, ALARM_on);
  NTP_update_timerID = timer.setInterval(120 * 1000L, NTP_update);

  timeClient.begin();
  pixels.begin();

  Blynk.virtualWrite(V0, red);
  Blynk.virtualWrite(V1, green);
  Blynk.virtualWrite(V2, blue);
  Blynk.virtualWrite(V5, "First Start");
  Blynk.virtualWrite(V8, ALARM_timer);
  Blynk.virtualWrite(V11, ALARM_start, 0, 0, ALARM_days_start);
  Blynk.virtualWrite(V14, ALARM_effect);
  Blynk.virtualWrite(V20, TIME_sync_timer);
  if (debug) {
    Blynk.virtualWrite(V21, 1);
  }
  else {
    Blynk.virtualWrite(V21, 2);
  }
  timeClient.update();
  timer.disable(ALARM_on_timerID);
  attachInterrupt(PIR_pin, PIN_change, CHANGE);
  digitalWrite(PIR_pin, LOW);
}

void loop()
{
  timer.run();
  Blynk.run();
}

void ICACHE_RAM_ATTR PIN_change()
{
  if (digitalRead(PIR_pin == HIGH)) {
    if (debug) {
      Serial.println("PIN Change with PIR done");
    }
    PIR_time_check();
  }
  else {
    if (timeClient.getHours() * 60 * 60 + timeClient.getMinutes() * 60 + timeClient.getSeconds() > PIR_end_time) {
      clearStrip();
      timer.disable(PIR_on_timerID);
    }
  }
}

void WIFI_check()
{
  if (WiFi.status() != WL_CONNECTED) {
    WiFi.begin(ssid, pass);
    Serial.println("Lost WiFi Connection, trying to reconnect... ");
    connectionSuccess = 1;
  }
  else if (WiFi.status() == WL_CONNECTED && connectionSuccess == 1) {
    Serial.println("WiFi connected");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    Serial.println("Wifi works, now try Blynk connection");
    bdelay(2000);
    Blynk.config(auth, serv, 8080);
    Blynk.connect(30 * 1000);
    if (Blynk.connected() == true) {
      bdelay(2000);
      Blynk.run();
      Serial.println("Blynk is online");
    }
    connectionSuccess = 0;
  }
}

void NTP_update()
{
  timeClient.update();
  if (debug) {
    Serial.println("Syncing NTP time");
  }
}

/* not needed anymore
   void TIME_check()
  {

  ALARM_time_check();
  //PIR_time_check(); // outcommented because of softwareinterrupt
  }*/

void WDAY_and_NOW()
{
  weekday = timeClient.getDay();
  if (debug) {
    Serial.println(String("Zeit: ") + timeClient.getHours() + ":" + timeClient.getMinutes());
  }
  if (timeClient.getDay() == 0) {
    weekday = 7;
  }

  now = timeClient.getHours() * 60 * 60 + timeClient.getMinutes() * 60;
}

/*****************************************************
  ALARM Time Check
*****************************************************/
void ALARM_time_check()
{
  WDAY_and_NOW();
  if (now == ALARM_start && ALARM_days[weekday] && ALARM_start >= 0) {
    clearStrip();
    ALARM_end_time = now + ALARM_timer + timeClient.getSeconds();
    timer.enable(ALARM_on_timerID);
    //ALARM_on_timerID = timer.setTimer(1000, ALARM_on, ALARM_timer);
    if (debug) {
      Serial.println(String("ALARM Effect started at: ") + timeClient.getHours() + ":" + timeClient.getMinutes() + ":" + timeClient.getSeconds());
    }
  }
}

void ALARM_on()
{
  if (timeClient.getHours() * 60 * 60 + timeClient.getMinutes() * 60 + timeClient.getSeconds() <= ALARM_end_time) {
    if (ALARM_effect == 1) {
      ZeRGBa();
    } else if (ALARM_effect == 2) {
      colorWipe(10, 45, 50, 50);
      colorWipe(0, 0, 0, 50);
    } else if (ALARM_effect == 3) {
      CylonBounce(255, 0, 0, 4, 20, 60);
    } else if (ALARM_effect == 4) {
      meteorRain(255, 127, 127, 10, 64, true, 30);
    } else if (ALARM_effect == 5) {
      Strobe(255, 0, 255, 10, 50, 1000); // Slower: Strobe(0xff, 0x77, 0x00, 10, 100, 1000);
    }
  }
  else {
    if (debug) {
      Serial.println(String("ALARM Effect ended at: ") + timeClient.getHours() + ":" + timeClient.getMinutes() + ":" + timeClient.getSeconds());
    }
    timer.disable(ALARM_on_timerID);
  }
}

/*****************************************************
  PIR Time Check START
*****************************************************/
void PIR_time_check()
{
  if (debug) {
    Serial.println("PIR_time_check");
  }
  WDAY_and_NOW();
  if (PIR_days[weekday] && PIR_start >= 0 && PIR_stop >= 0) {

    // Time between 5:00 and 6:00 f.e.
    if (PIR_start < PIR_stop) {
      finish = PIR_stop;
    }

    // Time between 6:00 and 5:00 f.e.
    else if (PIR_start > PIR_stop) {
      finish = PIR_stop + 24 * 60 * 60;

      // Now is at time on the next day after Start
      // Start = 20:00 | Stop = 4:00 | Now = 2:00
      if (now < PIR_start) {
        now = now + 24 * 60 * 60;
      }
    }
  }

  // Check if Now is between Start and Stop
  if (now >= PIR_start && now < finish && !timer.isEnabled(ALARM_on_timerID)) {
    PIR_end_time = now + PIR_timer + timeClient.getSeconds();
    timer.enable(PIR_on_timerID);
    if (debug) {
      Serial.println("PIR Detection IS Active");
    }
  }
  else {
    timer.disable(PIR_on_timerID);
    if (debug) {
      Serial.println("PIR Detection NOT Active");
    }
  }
}

void PIR_on()
{
  if (debug) {
    Serial.println(String("PIR was running at: ") + timeClient.getHours() + ":" + timeClient.getMinutes() + ":" + timeClient.getSeconds());
  }
  if (timeClient.getHours() * 60 * 60 + timeClient.getMinutes() * 60 + timeClient.getSeconds() <= PIR_end_time) {
    if (PIR_effect == 1) {
      ZeRGBa();
    }
    else if (PIR_effect == 2) {
      colorWipe(50, 10, 45, 25);
      colorWipe(0, 0, 0, 25);
    }
    else if (PIR_effect == 3) {
      CylonBounce(255, 0, 0, 4, 20, 60);
    }
    else if (PIR_effect == 4) {
      meteorRain(255, 127, 127, 10, 64, true, 30);
    }
    else if (PIR_effect == 5) {
      Strobe(255, 0, 255, 10, 50, 1000); // Slower: Strobe(0xff, 0x77, 0x00, 10, 100, 1000);
    }
  }
  else {
    clearStrip();
    timer.disable(PIR_on_timerID);
  }
}

void bdelay(unsigned long milli)
{
  unsigned long end_time = millis() + milli;
  while (millis() < end_time)
  {
    if (Blynk.connected())
    {
      Blynk.run();
    }
    yield();
  }
}

/********************* LED Stuff ***********************/

void showStrip() {
  pixels.show();
}

void clearStrip() {
  pixels.fill(0, 0, NUM_LEDS);
  showStrip();
}

void setPixel(int Pixel, int red, int green, int blue) {
  pixels.setPixelColor(Pixel, pixels.Color(red, blue, green));
}

/********************* LED Effects *********************/

void ZeRGBa()
{
  for (int i = 0; i < NUM_LEDS; i++) {
    setPixel(i, red, green, blue);
    showStrip();
  }
}

void colorWipe(int red, int green, int blue, unsigned long SpeedDelay)
{
  if (colorWipeMillis - currentMillis >= SpeedDelay) {
    colorWipeMillis = currentMillis;
    for (int i = 0; i < NUM_LEDS; i++) {
      setPixel(i, red, green, blue);
      showStrip();
    }
  }
}

void CylonBounce(int red, int green, int blue, int EyeSize, int SpeedDelay, int ReturnDelay) {

  for (int i = 0; i < NUM_LEDS - EyeSize - 2; i++) {
    clearStrip();
    setPixel(i, red / 10, green / 10, blue / 10);
    for (int j = 1; j <= EyeSize; j++) {
      setPixel(i + j, red, green, blue);
    }
    setPixel(i + EyeSize + 1, red / 10, green / 10, blue / 10);
    showStrip();
    bdelay(SpeedDelay);
  }

  bdelay(ReturnDelay);

  for (int i = NUM_LEDS - EyeSize - 2; i > 0; i--) {
    clearStrip();
    setPixel(i, red / 10, green / 10, blue / 10);
    for (int j = 1; j <= EyeSize; j++) {
      setPixel(i + j, red, green, blue);
    }
    setPixel(i + EyeSize + 1, red / 10, green / 10, blue / 10);
    showStrip();
    bdelay(SpeedDelay);
  }

  bdelay(ReturnDelay);
}

void meteorRain(int red, int green, int blue, int meteorSize, int meteorTrailDecay, boolean meteorRandomDecay, unsigned long SpeedDelay) {
  clearStrip();

  for (int i = 0; i < NUM_LEDS + NUM_LEDS; i++) {

    // fade brightness all LEDs one step
    for (int j = 0; j < NUM_LEDS; j++) {
      if ( (!meteorRandomDecay) || (random(10) > 5) ) {
        fadeToBlack(j, meteorTrailDecay);
      }
    }

    // draw meteor
    for (int j = 0; j < meteorSize; j++) {
      if ( ( i - j < NUM_LEDS) && (i - j >= 0) ) {
        setPixel(i - j, red, green, blue);
      }
    }

    showStrip();
    bdelay(SpeedDelay);
  }
}

void fadeToBlack(int ledNo, byte fadeValue) {
  uint32_t oldColor;
  uint8_t r, g, b;

  oldColor = pixels.getPixelColor(ledNo);
  r = (oldColor & 0x00ff0000UL) >> 16;
  g = (oldColor & 0x0000ff00UL) >> 8;
  b = (oldColor & 0x000000ffUL);

  r = (r <= 10) ? 0 : (int) r - (r * fadeValue / 256);
  g = (g <= 10) ? 0 : (int) g - (g * fadeValue / 256);
  b = (b <= 10) ? 0 : (int) b - (b * fadeValue / 256);

  pixels.setPixelColor(ledNo, r, g, b);
}

void Strobe(int red, int green, int blue, int StrobeCount, int FlashDelay, int EndPause) {
  for (int j = 0; j < StrobeCount; j++) {
    for (int i = 0; i < NUM_LEDS; i++) {
      setPixel(i, red, green, blue);
      showStrip();
    }
    showStrip();
    bdelay(FlashDelay);
    clearStrip();
    bdelay(FlashDelay);
  }

  bdelay(EndPause);
}

/********************* LED Effects *********************/

BLYNK_WRITE(V0)
{
  red = param.asInt();
}

BLYNK_WRITE(V1)
{
  green = param.asInt();
}

BLYNK_WRITE(V2)
{
  blue = param.asInt();
}

BLYNK_WRITE(V8)
{
  ALARM_timer = param.asInt() * 1000;
}

BLYNK_WRITE(V11)
{
  TimeInputParam t(param);
  if (t.hasStartTime()) {
    ALARM_start = t.getStartHour() * 60 * 60 + t.getStartMinute() * 60 + t.getStartSecond();
  }
  else {
    ALARM_start = -1;
  }
  for (int i = 1; i <= 7; i++) {
    ALARM_days[i] = t.isWeekdaySelected(i);
  }
  ALARM_time_check();
}

BLYNK_WRITE(V14)
{
  switch (param.asInt()) {
    case 1: {
        //ZeRGBa
        ALARM_effect = 1;
        break;
      }
    case 2: {
        //colorWipe
        ALARM_effect = 2;
        break;
      }
    case 3: {
        //Cylon
        ALARM_effect = 3;
        break;
      }
    case 4: {
        //Meteor
        ALARM_effect = 4;
        break;
      }
    case 5: {
        //Strobe
        ALARM_effect = 5;
        break;
      }
  }
}

BLYNK_WRITE(V20)
{
  TIME_sync_timer = param.asInt();
  if (debug) {
    Serial.println(String("Time Synchronisation every: ") + TIME_sync_timer + "Minutes");
  }
}

BLYNK_WRITE(V21)
{
  int debug_switch = param.asInt();
  if (debug_switch == 1) {
    debug = true;
  }
  else {
    debug = false;
  }
}

EDIT: Wow just same time you posted your answer :slight_smile: hehe
So ok, I don’t have to change the PIN D3. But my attached Interrupt won’t be run at all.
Is there anything special I have to do with that Interrupt on Wemos D1?

There was an error after every blynk connect while not having ICACHE_RAM_ATTR added to the PIN_change void.

I don’t understand your code at all I’m afraid, where is the interrupt and why are you still using a timer to poll the PIR pin?

Pete.

Ok, I try to shrink the code to mark the positions that are neccessary :slight_smile:

void setup() {
...
  WIFI_check_timerID = timer.setInterval(5 * 1000L, WIFI_check);
  ALARM_check_timerID = timer.setInterval(60 * 1000L, ALARM_time_check);
  ALARM_on_timerID = timer.setInterval(1 * 1000L, ALARM_on);
  NTP_update_timerID = timer.setInterval(120 * 1000L, NTP_update);

=> there is no PIR timer at all??????

...
attachInterrupt(PIR_pin, PIN_change, CHANGE); 
...
}

void ICACHE_RAM_ATTR PIN_change()
{
  if (digitalRead(PIR_pin == HIGH)) {
    if (debug) {
      Serial.println("PIN Change with PIR done");
    }
    PIR_time_check();
  }
  else {
    if (timeClient.getHours() * 60 * 60 + timeClient.getMinutes() * 60 + timeClient.getSeconds() > PIR_end_time) {
      clearStrip();
      timer.disable(PIR_on_timerID);
    }
  }
}

void PIR_time_check() {
...
the magic happen where I check if the actual time is between start and stop of blynk
if this is true, the timer for PIR_on is triggered
...
}

void PIR_on() {
...
LED effect started for given time in seconds from blynk slider widget
if time is over, the PIR_on timer will be disabled
...
}

Apologies, I think I scrolled up too far and was looking at your old code!

I think your syntax is wrong. This:

attachInterrupt(PIR_pin, PIN_change, CHANGE);

should be more like this:

attachInterrupt(digitalPinToInterrupt(PIR_pin), PIN_change, CHANGE);

It should then compile without the ICACHE_RAM_ATTR

Pete.

1 Like

yep that’s it.

oh and there was one more thing I forgot… pinMode(PIR_pin, INPUT); inside the setup void xD

Now I will try and report back.

nope same prob :frowning:

image

What version of the ESP core are you using?

Pete.

1 Like

Hey Pete,

I’m using this Version:

Maybe the settings of arduino IDE are interesting as well?

Pete.

1 Like

2.4.2 works :slight_smile:

I’m going to reduce my code now, the PIR worked in first tests.

2 Likes

Hey PeteKnight

I think it is running now as it should be :slight_smile:
Just one small issue I found out and don’t know why that happens.

An LED effect stops by accident and then some LEDs are still on.
In this case would it be OK to set a variable to true if the LED effect is running, and if time over, set it to false.

Inside of the loop I would check if this variable is false and if so set the LEDs to black (0,0,0).

Or does this belong to your debounce thing you mentioned earlier?

You wouldn’t need to do the check within the loop, you could use a timer instead.

The debounce routine is to prevent problems that occur when mechanical switches don’t make a perfectly clean on/off action. Instead you get lots of very fast on/off events within a few milliseconds when the contacts open and close. The debounce code ignores these very fast on/off events.

Pete.

ah ok, thats not the problem I have with the PIR. It sets the PIN to HIGH and then isn’t responsible to any motion for 2-3 Seconds.

My aim is to extend the time the LEDs will be on, if the motion is still detected after this known “downtime”.

ok pete, first night with all the changes made yesterday… I have an old new problem…

something seems to break the memory or anything else. my esp starts at some to restart. sometimes after the bootup… sometimes after minutes… sometimes it reconnects… sometimes it stays offline.

I think it belongs to the attached interrupt? but I’m not sure.
I know it could be realy hard to troubleshoot this, but where to start?

I retryed with actual board firmware and the ICACHE_RAM_ATTR, but that isn’t the problem.

I don’t understand what this means.

Pete.

The thing you mentioned yesterday. I don’t need the ICACHE thing for the PIN_change void if I use the older firmware 2.4.2.

Because of disconnects I tryed 2.5.2 with ICACHE, but that didn’t help so went back to 2.4.2.

Ok, I think I found the problem. It belongs to the attachedinterrupt :frowning:

If I do it the wrong way you all told me (I just did a try and error session) it runs without disconnect…
PIR is readout inside the loop and if 5 seconds are passed since last PIR = HIGH then the effect is started or checked if it is running and so on.

Ok guys, everything is working fine now.

I have just small cosmetic things to polish.
I have a Labeled Value for just text inside my Blynk app on V19.

That is a headline for my widget below this Value.
I just want to change the color for this widget.

I did this inside the void setup () and inside the BLYNK_WRITE(V19) and it doesn’t work.
Yeah I did the same thing for around 10 other widgets in this app and the color appears there, but not in V19 (I also tryed V40 instead).

How I did it:

BLYNK_WRITE(V19)
{
  Blynk.setProperty(V19, "color", "#33FF3C");
}

or this way:

void setup()
{
...
  Blynk.setProperty(V19, "color", "#33FF3C");
...
}

What am I doing wrong?