# Máy, thiết bị và sản phẩm khác > Điện - Điện tử - Tự động hóa > Arduino >  ESP8266 - dùng Blynk tắt mở theo điều kiện thời gian (time schedule)

## CKD

Chào các bạn!
Rất lâu rồi mới quay lại viết bài trên diễn đàn. Vì đã lâu không tham gia vào việc chế cháo cnc nên cũng không biết phải trao đổi thêm những gì.

Nay viết bài này cũng không liên quan gì đến CNC, mượn phòng Arduino để trao đổi/chia sẻ về việc lập trình ESP8266 một dự án mà mình có chia sẻ video trên youtube.

*Nhu cầu là dùng ESP8266, lập lịch hẹn và tắt mở (relay chẵng hạn) theo lịch đã được lập trước đó.*




Mô tả:

1. Lập trình Arduino, dùng phần cứng ESP8266. Kết nối qua wifi đến máy chủ Blynk.
2. Đăng ký tài khoản Blynk, tạo dự án mới (New Project), trong dự án đó có thêm vào
RTC (xem link) đã được config đúng múi giờ.
Time Input (xem link), tùy nhu cầu mà cài đặt, phần OUPUT đặt cho nó một Virtual PIN mà bạn muốn. Lưu ý xem thêm ấy cái ví dụ (VD 1, VD 2, VD 3)Thêm các Widgets hay ABC XYZ gì đó tùy bạn muốn.
3. Lập trình trên giao diện IDE cùa Arduino. Mấy phần khai báo, sử dụng, kết nối wifi, kết nối Blynk các bạn tự tham khảo thêm trên internet. Mình sẽ chỉ chi sẽ phần xử lý thời gian (time chedule) cho nó ngắn gọn.Khai báo sử dụng thư viện, có 2 thư viện cần cho việc này: WidgetRTC.h và TimeLib.h (xem link), ngoài ra còn các thư viện khác để có thể sử dụng ESP8266 và Blynk, các bạn tự thêm vào  :Big Grin: 


```
// khai báo biến cần dùng
unsigned int TimeStart, TimeStop;
byte dayStartSelect = 0;
byte dayStopSelect  = 0;

// timeInput là Virtual PIN mà mình đã setting trong dự án Blynk
// đoạn chương trình bên dưới sẽ tự động làm việc mỗi khi có sự thay đổi Time Input trên app Blynk

BLYNK_WRITE(timeInput)
{
  TimeInputParam t(param); //blynk-library/src/WidgetTimeInput.h

  // Process start time
  if (t.hasStartTime())
  {
    TimeStart = t.getStartHour()*60 + t.getStartMinute();     
  }
  else TimeStart = 0;

  // Process stop time

  if (t.hasStopTime())
  {
    TimeStop = t.getStopHour()*60 + t.getStopMinute();
  }
  else TimeStop = 0;
  
  // Process Blynk TimeInput weekdays (1. Mon, 2. Tue, 3. Wed, 4. Thu, 5. Fri, 6. Sat, 7. Sun)
  // Process RTC weekday();           (1. Sun, 2. Mon, 3. Tue, 4. Wed, 5. Thu, 6. Fri, 7. Sat) 
  // Bit dayStart/StopSelect  (0. None 1. Sun, 2. Mon, 3. Tue, 4. Wed, 5. Thu, 6. Fri, 7. Sat)
  dayStartSelect = 0;
  dayStopSelect  = 0;
  for (int i = 1; i <= 7; i++)
  {
    if (t.isWeekdaySelected(i))
    {
      if (i == 7)        bitWrite(dayStartSelect, 1,   1);
      else               bitWrite(dayStartSelect, i+1, 1);
      if (TimeStop < Time1Start)
        if (i == 6)      bitWrite(dayStopSelect,  1,   1);
        else if (i == 7) bitWrite(dayStopSelect,  2,   1);
        else             bitWrite(dayStopSelect,  i+2, 1);
      else
        if (i == 7)      bitWrite(dayStopSelect,  1,   1);
        else             bitWrite(dayStopSelect,  i+1, 1);
    }
  }
}
```





```
// khai báo biến cần dùng
bool timeOnOff      = false;

// đoạn chương trình sẽ kiểm tra điều kiện và bật/tắt biến timeOnOff

void TimeAuto()
{
  unsigned int times = hour()*60 + minute();
  byte today = weekday(); //the weekday now (Sunday is day 1, Monday is day 2) 

  if (bitRead(dayStartSelect, today))
  {
    if (TimeStart == TimeStop)    timeOnOff = false;
    else if (TimeStart <=  times) timeOnOff = true;    
  }
  if (bitRead(dayStopSelect,  today))
  {
    if ((TimeStart < TimeStop ) && (TimeStop <= times))                                     timeOnOff = false;
    if ((TimeStop  < TimeStart) && (TimeStop <= times) && (times < TimeStart))              timeOnOff = false;
    if ((TimeStop  < TimeStart) && !bitRead(dayStartSelect, today) && (TimeStop <= times))  timeOnOff = false;
  }
}
```


void *TimeAuto()* sẽ đặt vào trong void loop của chương trình chính.
kiểm tra biến *timeOnOff* và thực thi công việc tùy theo nhu cầu (tắt mở relay chẵng hạn)
Chúc các bạn thành công!

----------

anhcos, Gamo, huynhbacan, nhatson

----------


## CKD

Cập nhật full code để anh em thử nghiệm nhe.
Soft dùng Arduino IDE
Hardware dùng ESP8266 V1



```
/*************************************************************
 * Ví dụ tắt mở theo lịch hẹn trước
 * Người viết: Phạm Duy Anh - phamduyanh@gmail.com
 * Chia sẻ trên: cncprovn.com và youtube
 * 
 * Ví dụ được tạo từ Example -> Blynk -> Boards_Wifi -> ESP8266_Standalone
 * Mạch dùng ESP8266 V1, dung lượng 1MB, không có LED built IN.
 * Do đó mình sẽ dùng widget displays LED để hiển thị trạng thái biến timeOnOff
 * Đồng thời dùng Serial Monitor để theo dõi trạng thái TimeInput và timeOnOff
 * 
 * Thư viện cần thêm:
 *  #include <TimeLib.h>
 *  #include <WidgetRTC.h>
 *  
 * Thay đổi theo nhu cầu các biến:
 *  YourAuthToken
 *  YourNetworkName
 *  YourPassword
 *  #define LEDV      V0  // hiển thị trạng thái biến timeOnOff
 *  #define timeShow  V1  // hiển thị thời gian thự của thiết bị
 *  #define timeInput V2  // nhận dữ liệu cài đặt thời gian
 *  
 * Khởi tạo các Widget
 *  WidgetLED LED(LEDV);
 *  WidgetRTC rtc;
 *  
 * Chương trình
 *  showTime()            // hiển thị thời gian thự của thiết bị mỗi giây một lần
 *  TimeAuto()            // so sánh thời gian thực và lịch cài đặt
 *  twoDigits(int digits) // chuyển đổi thành 2 ký tự
 *  
 * Lưu ý: 
 *  Việc truy cập blynk chỉ nên thực hiện mỗi giây/lần hoặc chậm hơn.
 *  
 *************************************************************/

/* Comment this out to disable prints and save space */
#define BLYNK_PRINT Serial

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <TimeLib.h>                //https://github.com/PaulStoffregen/Time
#include <WidgetRTC.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "YourAuthToken";

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "YourNetworkName";
char pass[] = "YourPassword";

// khai báo biến cần dùng
bool timeOnOff      = false;
bool oldtimeOnOff;
bool isFirstConnect;
unsigned int TimeStart, TimeStop;
byte dayStartSelect = 0;
byte dayStopSelect  = 0;

int oldSecond, nowSecond;

#define LEDV      V0  // hiển thị trạng thái biến timeOnOff
#define timeShow  V1  // hiển thị giờ thật của thiết bị
#define timeInput V2  // nhận dữ liệu cài đặt thời gian
#define PinOut    2   //led built in

WidgetLED LED(LEDV);
WidgetRTC rtc;

void setup()
{
  // Debug console
  Serial.begin(9600);
  Blynk.begin(auth, ssid, pass);
  
  pinMode(PinOut, OUTPUT);
  digitalWrite(PinOut, !timeOnOff);
}

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

BLYNK_CONNECTED()
{
  Blynk.syncAll();
  rtc.begin();
}

// timeInput là Virtual PIN mà mình đã setting trong dự án Blynk
// đoạn chương trình bên dưới sẽ tự động làm việc mỗi khi có sự thay đổi Time Input trên app Blynk

BLYNK_WRITE(timeInput)
{
  Serial.println("Time Input");
  TimeInputParam t(param); //blynk-library/src/WidgetTimeInput.h

  // Process start time
  if (t.hasStartTime())
  {
    TimeStart = t.getStartHour()*60 + t.getStartMinute();     
  }
  else TimeStart = 0;

  // Process stop time

  if (t.hasStopTime())
  {
    TimeStop = t.getStopHour()*60 + t.getStopMinute();
  }
  else TimeStop = 0;
  
  // Process Blynk TimeInput weekdays (1. Mon, 2. Tue, 3. Wed, 4. Thu, 5. Fri, 6. Sat, 7. Sun)
  // Process RTC weekday();           (1. Sun, 2. Mon, 3. Tue, 4. Wed, 5. Thu, 6. Fri, 7. Sat) 
  // Bit dayStart/StopSelect  (0. None 1. Sun, 2. Mon, 3. Tue, 4. Wed, 5. Thu, 6. Fri, 7. Sat)
  dayStartSelect = 0;
  dayStopSelect  = 0;
  for (int i = 1; i <= 7; i++)
  {
    if (t.isWeekdaySelected(i))
    {
      if (i == 7)        bitWrite(dayStartSelect, 1,   1);
      else               bitWrite(dayStartSelect, i+1, 1);
      if (TimeStop < TimeStart)
        if (i == 6)      bitWrite(dayStopSelect,  1,   1);
        else if (i == 7) bitWrite(dayStopSelect,  2,   1);
        else             bitWrite(dayStopSelect,  i+2, 1);
      else
        if (i == 7)      bitWrite(dayStopSelect,  1,   1);
        else             bitWrite(dayStopSelect,  i+1, 1);
    }
  }
}

// đoạn chương trình sẽ kiểm tra điều kiện và bật/tắt biến timeOnOff

void TimeAuto()
{
  unsigned int times = hour()*60 + minute();
  byte today = weekday(); //the weekday now (Sunday is day 1, Monday is day 2) 

  if (bitRead(dayStartSelect, today))
  {
    if (TimeStart == TimeStop)    timeOnOff = false;
    else if (TimeStart <=  times) timeOnOff = true;    
  }
  if (bitRead(dayStopSelect,  today))
  {
    if ((TimeStart < TimeStop ) && (TimeStop <= times))                                     timeOnOff = false;
    if ((TimeStop  < TimeStart) && (TimeStop <= times) && (times < TimeStart))              timeOnOff = false;
    if ((TimeStop  < TimeStart) && !bitRead(dayStartSelect, today) && (TimeStop <= times))  timeOnOff = false;
  }
}

void showTime()
{
  nowSecond = second();
  if (oldSecond != nowSecond)
  {
    oldSecond = nowSecond;
    
    String currentTime;
    if (isPM()) currentTime = twoDigits(hourFormat12()) + ":" + twoDigits(minute()) + ":" + twoDigits(second()) + " PM";
    else        currentTime = twoDigits(hourFormat12()) + ":" + twoDigits(minute()) + ":" + twoDigits(second()) + " AM";
    Blynk.virtualWrite(timeShow, currentTime);

    if (oldtimeOnOff != timeOnOff)
    {
      if (timeOnOff)
      {
        LED.on();
        digitalWrite(PinOut, !timeOnOff);
        Serial.println("Time schedule is ON");
      }
      else
      {
        LED.off();
        digitalWrite(PinOut, !timeOnOff);
        Serial.println("Time schedule is OFF");
      }
      oldtimeOnOff = timeOnOff;
    }
  }
}

String twoDigits(int digits)
{
  if(digits < 10) return "0" + String(digits);
  else            return       String(digits);
}
```

----------

anhcos

----------


## anhcos

Ngon quá Duy Anh, code này quá phù hợp vì  a đang làm cái nhà kính với giàn năng lượng xanh, để demo là chủ yếu.
Xài một số cảm biến nhiệt độ, độ ẩm bức xạ,... cả nồng độ khí Co2 nữa do nhà là hệ thống kín.

----------


## CKD

> Ngon quá Duy Anh, code này quá phù hợp vì  a đang làm cái nhà kính với giàn năng lượng xanh, để demo là chủ yếu.
> Xài một số cảm biến nhiệt độ, độ ẩm bức xạ,... cả nồng độ khí Co2 nữa do nhà là hệ thống kín.


A dùng cảm biến gì thế?

----------


## anhcos

Cứ mỗi cảm biến sẽ mua cả cái phổ thông và cái tốt để so sánh và lấy dữ liệu về rồi tinh chỉnh sao cho ổn định.
VD đo độ ẩm/nhiệt độ thì mua cả cái chỉ có nhiệt độ, cái chỉ có độ ẩm và cả loại kết hợp.

----------


## CKD

Nội dung code phía trên có một lỗi là khi nó đang vận hành mà mình vọc cái Time Input thì có một số trường hợp nó chạy sai.
Mình đã viết lại đoãn code mới với cách giải kiểu khác. Kết quả đã sửa (fix) được lỗi trên.

Ngày trong tuần nếu được chọn thì sẽ là ngày để mở.
Thời gian cài đặt mở sẽ so sánh với thời gian thực. Thời gian tắt thì tùy, nếu thời gian tắt có giá trị bé hơn thời gian mở thì sẽ là trường hợp mở vào tối hôm nay và tắt vào sáng ngày mai. Tính năng này cũng đã được kiểm tra Ok.

Lỗi cụ thể thế nào thì các bạn xem clip nhe  :Big Grin: 




Phần code thế này, copy & pate thay thế cho cả cái function *BLYNK_WRITE(timeInput)* và *TimeAuto()*

copy & pate *TimeAuto()*


```
void TimeAuto()
{
  unsigned int times = hour()*60 + minute();
  byte today = weekday(); //the weekday now (Sunday is day 1, Monday is day 2) 

  if (TimeStart == TimeStop)
  {
    timeOnOff = false;
  }
  else if (TimeStart < TimeStop)
    if (bitRead(dayStartSelect, today - 1))
      if ((TimeStart <=  times) && (times < TimeStop))                      timeOnOff = true;
      else                                                                  timeOnOff = false;    
    else                                                                    timeOnOff = false;
  else if (TimeStart > TimeStop)
  {
    if      ((TimeStop  <=  times) && (times < TimeStart))                  timeOnOff = false;
    else if ((TimeStart <=  times) && bitRead(dayStartSelect, today - 1))   timeOnOff = true;
    else if ((TimeStop  >   times) && bitRead(dayStopSelect,  today))       timeOnOff = true; 
  }
}
```


copy & pate *BLYNK_WRITE(timeInput)*


```
BLYNK_WRITE(timeInput)
{
  Serial.println("Time Input");
  TimeInputParam t(param); //blynk-library/src/WidgetTimeInput.h

  // Process start time
  if (t.hasStartTime())
  {
    TimeStart = t.getStartHour()*60 + t.getStartMinute();     
  }
  else TimeStart = 0;

  // Process stop time

  if (t.hasStopTime())
  {
    TimeStop = t.getStopHour()*60 + t.getStopMinute();
  }
  else TimeStop = 0;
  

  // Process Blynk TimeInput weekdays                   7. Sun, 1. Mon, 2. Tue, 3. Wed, 4. Thu, 5. Fri, 6. Sat
  // Process RTC weekday();                             1. Sun, 2. Mon, 3. Tue, 4. Wed, 5. Thu, 6. Fri, 7. Sat
  // Bit dayStartSelect                                 0. Sun, 1. Mon, 2. Tue, 3. Wed, 4. Thu, 5. Fri, 6. Sat
  // Bit dayStopSelect = dayStartSelec + 1     0. None, 1. Sun, 2. Mon, 3. Tue, 4. Wed, 5. Thu, 6. Fri, 7. Sat
  dayStartSelect = 0;
  dayStopSelect  = 0;
  for (int i = 1; i <= 7; i++)
    if (t.isWeekdaySelected(i))
      if (i == 7)
      {
        bitWrite(dayStartSelect, 0,   1);
        bitWrite(dayStopSelect,  1,   1);
      }
      else
      {
        bitWrite(dayStartSelect, i, 1);
        bitWrite(dayStopSelect,  i+1, 1);
      }
}
```

----------

anhcos, biết tuốt, katerman, XLong

----------

