IOT Blind Opener Alarm Clock

I need some help. I have been using a lot of the codes posted out there to create my Frankenstein code. The objective is to use the blynk app to control a stepper motor to open my blinds. I would like to have two modes, one for manual control and the second as an alarm. (It will be nice to wake up too!!)

Setup:
Arduino Uno
ESP8266
Adafruit Motor Shield V2
Stepper Motor

I have a working code, that connects to the blynk server, controls the stepper motors (Manual Mode). The part I am stuck at now is making the alarm work. I saw was using the post with Costas great example. But now I am at the last steps and running low on my memory on my Arduino UNO. I could use some help to:

  1. condense my code to run more efficiently
  2. get the alarm to trigger.

#define BLYNK_PRINT Serial
#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>
char auth[] = "XXX";
char ssid[] = "XXX";            // your network SSID (name)
char pass[] = "XXX"      // your network password
char Date[16];
char Time[16];
#include <SoftwareSerial.h>
SoftwareSerial EspSerial(2, 3); // RX, TX
#define ESP8266_BAUD 9600
ESP8266 wifi(&EspSerial);
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_MS_PWMServoDriver.h"
//#include <SimpleTimer.h>
//#include  <Time.h>
//#include <TimeLib.h>
#include <WidgetRTC.h>
Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 
Adafruit_StepperMotor *myMotor = AFMS.getStepper(200, 2);
WidgetLCD lcd(V0);
#define VIRTUAL_SLIDER_POSITION     V1
#define VIRTUAL_OPEN_PIN            V2
#define VIRTUAL_CLOSED_PIN          V3
#define VIRTUAL_ALARM_PIN           V6



long startseconds;            // start time in seconds
long stopseconds;             // stop  time in seconds
long nowseconds;              // time now in seconds

//int btnpin;
SimpleTimer timer;
WidgetRTC rtc;


//bool isFirstConnect = true;   // Keep this flag not to re-sync on every reconnection
//BLYNK_CONNECTED() {          // This function will run every time Blynk connection is established
// if (isFirstConnect) {
    // Request Blynk server to re-send latest values for all pins
 //  Blynk.syncAll();
 //  isFirstConnect = false;
 // }
//}


void setup()
{
  Serial.begin(9600);
  delay(10);
  EspSerial.begin(ESP8266_BAUD);
  delay(10);
  Blynk.begin(auth, wifi, ssid, pass);
  AFMS.begin();  // create with the default frequency 1.6KHz
  myMotor->setSpeed(1000);  // 100 rpm   
  lcd.clear();
  lcd.print(0,0,"Blynk Connected");
  delay(50);
  updatedisplay();
  rtc.begin();
  //timer.setInterval(60000L, activetoday);
}




BLYNK_WRITE(V6) {  

    TimeInputParam t(param);
    int dayadjustment = -1;

if (t.hasStartTime())
  {
    Serial.println(String("Start: ") +
                   t.getStartHour() + ":" +
                   t.getStartMinute() + ":" +
                   t.getStartSecond());
  }

  
if(weekday() == 1){
        dayadjustment =  6; // needed for Sunday, Time library is day 1 and Blynk is day 7
    }

if(t.isWeekdaySelected((weekday() + dayadjustment))){ //Time library starts week on Sunday, Blynk on Monday
         nowseconds = ((hour() * 3600) + (minute() * 60) + second());
         startseconds=(t.getStartHour() * 3600) + (t.getStartMinute() * 60);
Serial.println(nowseconds);
Serial.println(startseconds);

      if(nowseconds >= startseconds){
                   
                   if(nowseconds <= startseconds + 90){    // 90s on 60s timer ensures 1 trigger command is sent
                          Serial.println("Alarmon!");           
                    }
          }
         }
}

 /*      
 void activetoday(){        // check if schedule should run today   
     if(year() != 1970){
        Blynk.syncVirtual(V6); // sync timeinput widget
     }
     }

  */  


 BLYNK_WRITE(VIRTUAL_OPEN_PIN)
{
  int DOpen = param.asInt();
    if (DOpen==1)   { 
      OpenB();
      Blynk.run();
     // DOpen=0;
      Blynk.syncVirtual(VIRTUAL_OPEN_PIN);
      }
      } 



 BLYNK_WRITE(VIRTUAL_CLOSED_PIN)
{
  int DClose = param.asInt();
   if (DClose ==1)    { 
      CloseB();
      Blynk.run();
      DClose=0;
      Blynk.syncVirtual(VIRTUAL_CLOSED_PIN);
      }
      } 



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


int StepMotorPosition=0;
int StepperScale=2000;  //Need to Define
int MotorPosition= ((float)StepMotorPosition/StepperScale)*100;
int Steps=1000;       //Optimize

 void CloseB(void) {
  while((float)MotorPosition/StepperScale*100<=99){
  int rotation=FORWARD;
   myMotor->step(Steps, rotation, DOUBLE);
  MotorPosition=MotorPosition+Steps;
  updatedisplay();
  }
}


 void OpenB() {
 while((float)MotorPosition/StepperScale*100>=1){
  int rotation=BACKWARD;
   myMotor->step(Steps, rotation, DOUBLE);
  MotorPosition=MotorPosition-Steps;
  updatedisplay();
 }
}


void updatedisplay(){
  lcd.clear();
  lcd.print(0,0,"Blind Position:");
  lcd.print(0,1,(float)MotorPosition/StepperScale*100);
  lcd.print(3,1,"%   ");
  Blynk.run();
}

As you can see from my code, I have the simple timers commented out. As I am getting farther in the project, I am really strapped for memory on the Arduino UNO. As I am at the last steps of which I think for triggering the timer for the alarm, I am out of memory. I had difficulties earlier on trying to figure out how to make the push buttons update, so found an example to force the update with (below) and found success:

 BLYNK_WRITE(VIRTUAL_OPEN_PIN)
{
  int DOpen = param.asInt();
    if (DOpen==1)   { 
      OpenB();
      Blynk.run();
     // DOpen=0;
      Blynk.syncVirtual(VIRTUAL_OPEN_PIN);
      }
      } 

Please help. I’m new to this and my background is not in coding, its actually building the thing (mechanical).

I’m not going to berate your use of the UNO… primarily as I am a fellow Arduino user who is only recently switching over to ESP… but not abandoning the Arduinos any time soon :wink: But the UNO is a bit lacking on the memory side.

Since your sketch isn’t very big, I am guessing it is all the required libraries that are taking up your space. I believe declaring variables also takes extra space… try to use direct pin references instead of variables… harder to keep track of, but that’s where commenting helps (and that will not take program space).

Eliminate the #define BLYNK_PRINT Serial and Serial prints if able… stick with LCD or even just use Blynk App for display. Also, have you considered using Eventor for all your timing needs?.. as that might save you some memory as well.

but I am. As you are finding the Arduino’s are short of memory for modern IOT projects.

I did produce some cleaner code for scheduling of events recently that uses local rather than global variables and allows you to have many different schedules without additional code. The additional schedules are probably not required for your project though. The suggestions made by @Gunner should all help to reduce the required memory but some are obviously at the expense of features that you originally planned to have in the project.

When I compile your sketch as you currently have it the memory allocation is as follows:

Sketch uses 25796 bytes (79%) of program storage space. Maximum is 32256 bytes.
Global variables use 1497 bytes (73%) of dynamic memory, leaving 551 bytes for local variables. Maximum is 2048 bytes.

Without the physical LCD this improves quite a bit to:

Sketch uses 23098 bytes (71%) of program storage space. Maximum is 32256 bytes.
Global variables use 1453 bytes (70%) of dynamic memory, leaving 595 bytes for local variables. Maximum is 2048 bytes.

Without BLYNK_PRINT Serial it further improves to:

Sketch uses 21954 bytes (68%) of program storage space. Maximum is 32256 bytes.
Global variables use 1453 bytes (70%) of dynamic memory, leaving 595 bytes for local variables. Maximum is 2048 bytes.

Improved scheduler code, but not active, takes you to:

Sketch uses 21034 bytes (65%) of program storage space. Maximum is 32256 bytes.
Global variables use 1427 bytes (69%) of dynamic memory, leaving 621 bytes for local variables. Maximum is 2048 bytes.

There are perhaps a few more tweaks available that should just about get the project running but it’s going to be tight with an Uno.

I just broke down and bought a MEGA. Tonight, I will take another crack at it and reduce it down. I will update my progress. Thanks for the tips.

@Gunner I am not familiar with the eventer. Do you have any good examples?

Actually, I do not have an event widget.

Are you iOS?

Yes. I am going to guess that its for Android only.

Only on Android at the moment but it should become available for iOS in due course.

OK, So now I have a new problem. I tried to modify my original code to upload to my new MEGA, but it gave me an error that the [1617] ESP is not responding. So

So since it gave the errors, I went back to the UNO. Same pin configuration and the Blynk’s ESP8266_Shield example. I ran the following code, and I am able to connect.

Here is the BLYNK example modified for the MEGA board. I have also tried to use the softserial and no luck there.

#define BLYNK_PRINT Serial
#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>

char auth[] = "XXX";
char ssid[] = "XXX";            // your network SSID (name)
char pass[] = "XXX";        // your network password

// Hardware Serial on Mega, Leonardo, Micro...
#define EspSerial Serial1

// or Software Serial on Uno, Nano...
//#include <SoftwareSerial.h>
//SoftwareSerial EspSerial(2, 3); // RX, TX

// Your ESP8266 baud rate:
#define ESP8266_BAUD 9600
ESP8266 wifi(&EspSerial);

void setup()

{
  // Debug console
  Serial.begin(9600);
  delay(10);

  // Set ESP8266 baud rate
  EspSerial.begin(ESP8266_BAUD);
  delay(10);

  Blynk.begin(auth, wifi, ssid, pass);
}

void loop()

{
  Blynk.run();
}

So here is the UNO code that uploads and works.

ESP8266_Shield example from BLYNK website
*************************************************************/
/* Comment this out to disable prints and save space */
#define BLYNK_PRINT Serial


#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>


char auth[] = "XXX";
char ssid[] = "XXX;            // your network SSID (name)
char pass[] = "XXX";        // your network password

// Hardware Serial on Mega, Leonardo, Micro...
//#define EspSerial Serial1

// or Software Serial on Uno, Nano...
#include <SoftwareSerial.h>
SoftwareSerial EspSerial(2, 3); // RX, TX

// Your ESP8266 baud rate:
#define ESP8266_BAUD 9600

ESP8266 wifi(&EspSerial);

void setup()
{
  // Debug console
  Serial.begin(9600);

  delay(10);

  // Set ESP8266 baud rate
  EspSerial.begin(ESP8266_BAUD);
  delay(10);

  Blynk.begin(auth, wifi, ssid, pass);
}

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

That’s just part of the “fun” with the shield system. I found that some days it worked and others it didn’t, with no apparent change.
I can’t spot anything obviously wrong with what you have for the Mega and you should be able to use hard / soft serial.

Maybe a lose wire or something. I think if you stick with it for the next 6 weeks or so you have a chance of getting connected with the Mega :slight_smile:

Ok, I got it. The examples are a bit confusing, where it labels

// Hardware Serial on Mega, Leonardo, Micro...
#define EspSerial Serial1

On a mega, this is TX1, and RX1 not TX, and RX. We can label this as a brainfart/noob moment.

Funny… I had mine (ESP-01 & Mega) setup and working in a few hours (spent too much time trying to figure out how to flash the ESP… only to never bother as it had a current enough AT version - so, to date, I still don’t know how to flash one :stuck_out_tongue: ) And it has been running 24/7 as my testbench, uploading examples and testing them for… what… 2 months exactly today.

No worries… The extra HW Serial ports are a great feature on the Mega… but still took me years before I ever even used them :smiley:

PS, in case you haven’t noticed, @Costas really dislikes Arduino/ESP mashups… I, on the other hand, love them… primarily to bug him :stuck_out_tongue_winking_eye: … Ha! OK, actually I have been a longterm Arduino user and just don’t believe their time is up, even with IOT, but as with any mashup, they do add to the setup and troubleshooting.

Little jabs are what make the online community great. I read many comments from the both of you guys, so I was really excited when I saw both of your comments for help and in such a short time.

But I do have a question with the triggering of the time input part of @Costas / my original post (code). I do not quite get the part about adding 90 seconds to ensure 1 trigger. If I am using this as an alarm, I wouldn’t want a delay.

Also have you guys experienced any issue with the time input widget? I keep having to delete and read the widget to get it to send the data, between each upload iteration.

Overall, my Mega board is working so much better and seems to be more responsive.

Math makes me crazy… as does interpreting professionally written code :stuck_out_tongue: But after looking at it, it seems more a “keep from triggering more than once in a row” buffer of perhaps 30 seconds, assuming it is scanning for a trigger every minute?

Not sure I am following you on this one… the Time Input widget “sends” its data only once, each time it is “set”… the data is then calculated and stored as a variable in the code and presumably set to repeatedly trigger whenever it is time.

I played around with the +90 seconds, that makes sense after I changed the time interval to check every second. my goal is to trigger a stepper motor. What happens when I set the steps larger (time duration) than the check frequency, it steps the motor, stops updating theck time, until it is done stepping. At some point during the stepping, it glitches and pops displays “[245139] Heartbeat timeout”.

I am trying to trigger the motor, while also updating the remainder of the code.

void setup()
{
....
timer.setInterval(2000L, activetoday);
}

BLYNK_WRITE(VIRTUAL_ALARM_PIN) { 
...
Serial.println(String("Current Time: ") +
                   hour() + ":" +
                   minute() + ":" +
                   second());
Serial.println(nowseconds);   //for debugging
Serial.println(startseconds);  //for debugging

      if(nowseconds >= startseconds){
                   
                   if(nowseconds <= startseconds+60){    // 5s on 60s timer ensures 1 trigger command is sent
                          Serial.println("Alarmon!"); 
                           int rotation=BACKWARD;
                           myMotor->step(5000, rotation, DOUBLE);          
                    }
          }
         }
}


For the second point in my last post, I seemed to solve that problem with correctly placing the timer.

Yes that was the idea behind the code. In my new EziScheduler code at RF 433 Mhz on virtual pin doesn't want to send (with scheduling) - #5 by Costas the 90s is +/- 31s on a 60s loop.

Obviously this can be varied to suit but it wasn’t really intended for stepper motor control.

UPDATE:

I upgraded to the MEGA, I also incorporated the accelstepper.h library. Here is the updated code. My current problem or bug is once I upload it does not work consistently. Sometimes, it works fine, then it stops working then becomes laggy/ delayed.

I also have been struggling with getting the updatedisplay() to work. Once I add this in, after upload it does not function and the stepper motor moves about one step every second.

Do you guys have any suggestions for making it run more smoothly? Also how I can update the display/ the current position?

/* Comment this out to disable prints and save space */
#define BLYNK_PRINT Serial
#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>
char auth[] = "XXX";
char ssid[] = "XXX";            // your network SSID (name)
char pass[] = "XXX";        // your network password
char Date[16];
char Time[16];

#define EspSerial Serial1

//Use for Uno
//#include <SoftwareSerial.h>
//SoftwareSerial EspSerial(2, 3); // RX, TX
#define ESP8266_BAUD 9600
ESP8266 wifi(&EspSerial);
#include <AccelStepper.h>
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_MS_PWMServoDriver.h"
#include <SimpleTimer.h>
#include  <Time.h>
#include <TimeLib.h>
#include <WidgetRTC.h>
Adafruit_MotorShield AFMStop(0x60); // Default address, no jumpers
Adafruit_StepperMotor *myStepper2 = AFMStop.getStepper(200, 2);
WidgetLCD lcd(V0);
#define VIRTUAL_SLIDER_POSITION     V1
#define VIRTUAL_OPEN_PIN            V2
#define VIRTUAL_CLOSED_PIN          V3
#define VIRTUAL_ALARM_PIN           V6
#define VIRTUAL_STOP_PIN            V7
#define INPUT_READ_INTERVAL 100 
#define BLYNK_INTERVAL 100 

long startseconds;            // start time in seconds
long stopseconds;             // stop  time in seconds
long nowseconds;              // time now in seconds
unsigned long last_input_time = 0;
int TotalPosSteps=500;  //Need to Define
int CurrentMotorPosition;

SimpleTimer timer;
WidgetRTC rtc;

/*  //Commented out for debugging, Since I don't have the display working I keep forgetting where the stepper is (Open/Closed)

bool isFirstConnect = true;   // Keep this flag not to re-sync on every reconnection
BLYNK_CONNECTED() {          // This function will run every time Blynk connection is established
 if (isFirstConnect) {
    // Request Blynk server to re-send latest values for all pins
  Blynk.syncAll();
  isFirstConnect = false;
 }
}
*/


void forwardstep2() {  
  myStepper2->onestep(FORWARD, DOUBLE);
}
void backwardstep2() {  
  myStepper2->onestep(BACKWARD, DOUBLE);
}

AccelStepper stepper2(forwardstep2, backwardstep2);


void setup()
{
  Serial.begin(9600);
  delay(10);
  EspSerial.begin(ESP8266_BAUD);
  delay(10);
  Blynk.begin(auth, wifi, ssid, pass);
  AFMStop.begin();  // create with the default frequency 1.6KHz
  //myMotor->setSpeed(1000);  // 100 rpm   
  stepper2.setMaxSpeed(2000);
  stepper2.setAcceleration(500);

  lcd.print(0,0,"Blynk Connected");
  delay(50);
  updatedisplay();
  rtc.begin();
  timer.setInterval(5000L, activetoday);     // check every 5s if schedule should run today
  //timer.setInterval(30000L, reconnectBlynk);  // check every 30s if still connected to server
  //timer.setInterval(2000L, updatedisplay);
}




 void activetoday(){        // check if schedule should run today   
     if(year() != 1970){
        Blynk.syncVirtual(VIRTUAL_ALARM_PIN); // sync timeinput widget
       }
     }

   void reconnectBlynk() {
    if (!Blynk.connected()) {
      if(Blynk.connect()) {
          BLYNK_LOG("Reconnected");
      }
      else {
          BLYNK_LOG("Not reconnected");
      }
 }
}


BLYNK_WRITE(VIRTUAL_ALARM_PIN) {  

    TimeInputParam t(param);
    int dayadjustment = -1;

if (t.hasStartTime())
  {
    Serial.println(String("Start: ") +
                   t.getStartHour() + ":" +
                   t.getStartMinute() + ":" +
                   t.getStartSecond());
  }

if(weekday() == 1){
        dayadjustment =  6; // needed for Sunday, Time library is day 1 and Blynk is day 7
    }

if(t.isWeekdaySelected((weekday() + dayadjustment))){ //Time library starts week on Sunday, Blynk on Monday
         nowseconds = ((hour() * 3600) + (minute() * 60) + second());
         startseconds=(t.getStartHour() * 3600) + (t.getStartMinute() * 60);
 
Blynk.virtualWrite(V1,String("Current Time: ") +
                   hour() + ":" +
                   minute() + ":" +
                   second());
Serial.println(String("Current Time: ") +
                   hour() + ":" +
                   minute() + ":" +
                   second());
Serial.println(nowseconds);
Serial.println(startseconds);

      if(nowseconds >= startseconds){
                   
                   if(nowseconds <= startseconds+20){    // 60s on 60s timer ensures 1 trigger command is sent
                           Serial.println("Alarmon!");
                           int rotation=BACKWARD;
                             lcd.clear();
                             lcd.print(0,0,"GOOD MORNING");
                             stepper2.moveTo(-TotalPosSteps);
                             stepper2.run();
                            // myMotor->step(1000, rotation, DOUBLE);          
                    }
          }
         }
}



     





 BLYNK_WRITE(VIRTUAL_OPEN_PIN)
{
  int DOpen = param.asInt();
    if (DOpen==1)   { 
      //OpenB();
      stepper2.moveTo(-TotalPosSteps);
      //updatedisplay();
      //Blynk.run();
      //Blynk.syncVirtual(VIRTUAL_OPEN_PIN);
      }
      } 


 BLYNK_WRITE(VIRTUAL_CLOSED_PIN)
{
  int DClose = param.asInt();
   if (DClose ==1)    { 
      //CloseB();
      stepper2.moveTo(TotalPosSteps);
      //updatedisplay();
      //Blynk.run();
      //Blynk.syncVirtual(VIRTUAL_CLOSED_PIN);
      }
      } 

 BLYNK_WRITE(VIRTUAL_STOP_PIN)
{
  int DStop = param.asInt();
   if (DStop ==1)    { 
      stepper2.stop();
      stepper2.setSpeed(0); 
      //Blynk.run();
      //Blynk.syncVirtual(VIRTUAL_CLOSED_PIN);
      }
      } 


void loop()
{
    if (millis() - last_input_time > BLYNK_INTERVAL) {
      last_input_time=millis();
      Blynk.run();
      
    }

    stepper2.run();
    timer.run();
      }


//int StepMotorPosition=0;
//int StepperScale=2000;  //Need to Define
//int MotorPosition= ((float)StepMotorPosition/StepperScale)*100;
//int Steps=1000;       //Optimize

  /* 
 void CloseB(void) {
 while((float)MotorPosition/StepperScale*100<=99){
  int rotation=FORWARD;
  myMotor->step(Steps, rotation, DOUBLE);
  MotorPosition=MotorPosition+Steps;
  updatedisplay();
  }    
    }   */


 /* 
 void OpenB() {
 while((float)MotorPosition/StepperScale*100>=1){
  int rotation=BACKWARD;
  myMotor->step(Steps, rotation, DOUBLE);
  MotorPosition=MotorPosition-Steps;
  updatedisplay();
 }   
   }     */


void updatedisplay(){
  lcd.clear();
  lcd.print(0,0,"Blind Position:");
  lcd.print(0,1,(float)stepper2.currentPosition()/TotalPosSteps*100);
  //lcd.print(0,1,(float)MotorPosition/StepperScale*100);
  lcd.print(3,1,"%   ");
  //Blynk.run();
}

In case someone hasn’t brought this up:
Nooooooooooo! Never ever ever do this, you will make every programmer cry!

Use define instead, the compiler will change all references automatically for you. Only useful when you don’t need to apply maths and/or resave the variable :stuck_out_tongue:

“#define is a useful C component that allows the programmer to give a name to a constant value before the program is compiled. Defined constants in arduino don’t take up any program memory space on the chip. The compiler will replace references to these constants with the defined value at compile time.”

I suspect programmers cry every time I even open the Arduino IDE :stuck_out_tongue_winking_eye:

But thank you for the #define tidbit… I have learned something new again :+1:

1 Like

For the most part I have used the define function, correct? I have initially assigned the reference pins and the time intervals.
Do you guys have any suggestions for debugging to the architecture to update the display for the .currentPosition().