مشروعات تطبيقية · الدرس 4.5
ساعة منبه متعددة الوظائف
العودة إلى الدروسوظائف المشروع والأزرار
نوسع مؤقت 3.9 إلى منبه متعدد الوظائف باستخدام شاشة أربع خانات (4 Digit 7-Segment Display) و RTC وحساس الحرارة والرطوبة (DHT11 Temperature and Humidity Module) و RGB وغيرها.
المكونات المطلوبة
- لوحة أردوينو (ELEGOO UNO R3) وطنان نشط (Active Buzzer).
- حساس الحرارة والرطوبة (DHT11 Temperature and Humidity Module) ووحدة ساعة الوقت الحقيقي (Real Time Clock Module).
- شاشة أربع خانات (4 Digit 7-Segment Display) ولوحة الحساسات (ALL IN ONE Sensor Shield).
الأزرار
زر الدقائق يزيد دقيقة في المؤقت، أو ساعة في إعداد المنبه. زر الثواني يزيد ثانية في المؤقت، أو دقيقة في المنبه. الاستمرار في الضغط يفعّل الزيادة المتتابعة السريعة.
زر الوظيفة يبدأ العد بعد ضبطه، ويضغط مجددًا أثناء العد لمسح الوقت. عند انتهاء العد يصدر الطنان صوتًا ويتغير RGB ويومض؛ الضغط بعد الإنذار يوقف الصوت والوميض ويمسح الوقت.
العرض وإعداد المنبه والتوصيل
زر تبديل العرض يتنقل بالضغط بين السنة ثم الشهر ثم الوقت ثم الحرارة ثم الرطوبة، ويكرر الدورة.
الإضافات إلى 3.9: جمع التاريخ والوقت والحرارة والرطوبة عبر RTC و DHT11؛ العودة إلى عرض الوقت بعد 5 ثوانٍ دون نشاط؛ حفظ المنبه في EEPROM ليبقى بعد انقطاع الطاقة؛ واستبدال LED بـ RGB.
ضبط المنبه
في شاشة الوقت اضغط زر الوظيفة. يتحول RGB إلى الأصفر وتظهر 0000 لإعداد المنبه. اضبطه ثم اضغط الوظيفة مجددًا. يبقى الضوء أصفر بعد تأكيد المنبه للدلالة على حفظه، أما الأخضر فيدل على عدم وجود منبه مضبوط.

لتشغيل المشروع، افتح ملف MyTimer.ino من مجلد هذا الدرس في بيئة Arduino IDE، مع إبقاء بقية ملفات المشروع في المجلد نفسه. بعد إتمام التوصيلات، وصّل لوحة أردوينو (UNO R3 Board) بالحاسوب بكابل توصيل (USB Cable)، ثم اضغط زر Upload (رفع) لنقل البرنامج إلى اللوحة وتشغيل الساعة والمنبه عليها.
وظائف العرض و DHT11
واجهة العرض
interface_display() تنظم الشاشة. تعرض display_num(uint16_t num) العد التنازلي والسنة والشهر واليوم، مع معالجة إظهار 0000 في عرض الوقت. تعرض temperature_display(uint16_t num) الحرارة بحرف C أخيرًا، و humidity_display(uint16_t num) الرطوبة بحرف H.
يعرض الشرط flag_alarm_set || function_clock استخدام حالتي إعداد المنبه أو عرض الساعة لاختيار العمل.
DHT11
تقيس measure_environment(float *temperature, float *humidity) البيئة، وتحدث update_dht11_value() بيانات الحرارة والرطوبة.
وظائف RGB والطنان والذاكرة والساعة
RGB
- rgb_yellow() أصفر لتنبيه إعداد المنبه.
- rgb_green() أخضر للتشغيل المعتاد دون منبه.
- rgb_light() تختار الأصفر أو الأخضر.
- close_rgb() تطفئ الإضاءة.
- rgb_color() تغير اللون تدريجيًا عند انتهاء الوقت.
الطنان
تفحص time_out() انتهاء الوقت؛ وعند الانتهاء يصدر الصوت وتتغير ألوان RGB عبر rgb_color().
EEPROM
تحفظ وقت المنبه وحالة الإضاءة. تستخدم eeprom_write_int(unsigned int address, unsigned int data) للكتابة، و eeprom_read_int(unsigned int address) للقراءة، و eeprom_clear_all(unsigned int eeprom_size) للمسح.
الساعة
تتواصل RTC عبر I2C، وقد تقطع المقاطعات المتكررة الاتصال وتعلق while في انتظار حدث I2C. ينقل المشروع وظائف مكتبة DS3231 ويضيف مهلة انتظار في الحلقات المعرضة لذلك. بعد Wire.requestFrom() تنتظر while(!Wire.available()) مع delay(1) وتزيد error_cnt. عند تجاوز 15 تصفره وتعود بالدالة بدل الانتظار المفتوح.
المراقبة ومقاطعة الزر
تضيف clock_readRegister8() أيضًا حد انتظار بعد طلب سجل واحد من RTC. لتجنب تعليق البرنامج يستخدم Watchdog: تهيئ setup() المراقبة بـ wdt_enable(WDTO_120MS)، وتحدث loop() العداد عبر wdt_reset(). وتستدعي حلقة rgb_color() التحديث كذلك.
زر الدقائق
تعمل button_s_interrupt() عند الحافة الهابطة. أول خطوة إزالة الارتداد باختبار ELIMINAT_THE_JITTER_OF_BUTTON_S والزمن من millis().
منطق زر الدقائق
بعد إزالة الارتداد يفحص الزر هل يبدأ وضع العد أو هل هو فيه بالفعل. يُمسح العرض والعدد عند الانتقال من الساعة أو السنة أو الشهر أو الحرارة أو الرطوبة، وإلا زيادة دقيقة.
يجب ألا يتجاوز المنبه 2359؛ عند الوصول للحد يعاد العدد إلى 0 في الإعداد. الحد الأعلى للعد 9999؛ ويستخدم المقتطف if(number >= 9900) number = 9999.
التبديل وزر الوظيفة
ينطبق المنطق المشابه على الزر الثاني. تستخدم button_display_change_interrupt() و switch(display_mode[press_cnt++]) اختيار DISPLAY_YEAR و DISPLAY_MONTH و DISPLAY_TIME و DISPLAY_TEMPERATURE و DISPLAY_HUNMIDITY.
button_choose_interrupt()
الوظيفة الأولى إيقاف المنبه عندما يرن: تمسح flag_alarm_ring، وتعود function_clock، وتصفّر alarm_value وتحفظ البيانات، وتعيد عدادات الانتظار والمهمة وتوقيت الزر.
الوظيفة الثانية بدء إعداد المنبه من وضع الساعة: تمسح number و alarm_value، وتعرض الأصفر، وتفعّل flag_alarm_set، وتحدث توقيت الزر.
تأكيد المنبه والمؤقت والمهام
الوظيفة الثالثة تأكيد المنبه: تفعّل flag_alarm، وتعطل flag_alarm_set، وتكتب number في EEPROM ثم تقرأ alarm_value، وتحفظ flag_alarm_set_ok في العنوان 3، وتعود لعرض الساعة وتحدث الانتظار وتوقيت الزر.
الوظيفة الرابعة تشغيل المؤقت أو إيقافه: تستدعي clock_to_timer() و rgb_light() ثم تقلب flag_begin وتحدث التوقيت.
Timer2
تنفذ timer2_interrupt() المهام التالية:
- task1_press_continuously() لفحص وضع الضغط المستمر دوريًا.
- task2_cout_down() لتحديد العد التنازلي وإنقاصه مرة في الثانية.
- task3_update_RTCtime() لتحديث بيانات الساعة دوريًا.
- task_reset() تعيد شاشة الوقت بعد 5 ثوانٍ دون عمل.
الكود
MyTimer/button.ino
void button_init(const uint8_t button_s,const uint8_t button_ms,const uint8_t button_choose,const uint8_t button_display_change)
{
pinMode(button_s, INPUT_PULLUP);
pinMode(button_ms, INPUT_PULLUP);
pinMode(button_choose, INPUT_PULLUP);
pinMode(button_display_change, INPUT_PULLUP);
}
void button_interrupt_init(const uint8_t button_s,const uint8_t button_ms,const uint8_t button_choose,const uint8_t button_display_change)
{
attachPCINT(digitalPinToPCINT(button_s), button_s_interrupt, FALLING );
attachPCINT(digitalPinToPCINT(button_ms), button_ms_interrupt, FALLING);
attachPCINT(digitalPinToPCINT(button_choose), button_choose_interrupt, FALLING );
attachPCINT(digitalPinToPCINT(button_display_change), button_display_change_interrupt, FALLING );
}
void button_s_interrupt()
{
time_button_s=millis();
if (ELIMINAT_THE_JITTER_OF_BUTTON_S)
{
flag_alarm = 0;
clock_to_timer();
waiting_time_cnt=1;
flag_begin=0;
number +=100;
last_time_button_s = time_button_s;
if(function_clock || flag_year || flag_month || flag_temperature || flag_humidity )
{
display_clear();
number = 0;
}
else if(flag_alarm_set && number >=2359)
{ number = 0;}
if (number >= 9900)
number = 9999;
}
}
void button_ms_interrupt()
{
time_button_ms=millis();
if (ELIMINAT_THE_JITTER_OF_BUTTON_MS)
{
flag_alarm = 0;
clock_to_timer();
waiting_time_cnt=1;
flag_begin=0;
number +=1;
if(function_clock || flag_year || flag_month || flag_temperature || flag_humidity )
{
display_clear();
number = 0;
}
else if(flag_alarm_set && number >=2359)
{ number = 0;}
if(number%100 == 60 && number != 60)
number = number/100*100+100;
if(number==60)
{number=100;}
if (number >= 9900)
number = 9999;
last_time_button_ms = time_button_ms;
}
}
void button_choose_interrupt ()
{
time_button_choose=millis();
if ( ELIMINAT_THE_JITTER_OF_BUTTON_CHOOSE &&
!(flag_year || flag_month || flag_temperature || flag_humidity) )
{
waiting_time_cnt = 1;
if(flag_alarm_ring)
{
flag_alarm_ring=0;
function_clock=1;
alarm_value = 0;
eeprom_write_int(0,number);
waiting_time_cnt=104;
task2_cnt = 20;
last_time_button_choose = time_button_choose;
}
else if( ( flag_year == 0 ) &&
( flag_month == 0 ) &&
( flag_temperature == 0 )&&
( flag_humidity == 0 ) &&
( function_clock ) &&
( flag_alarm == 1 ) && (flag_begin == 0) )
{
function_clock = 0;
number = 0;
alarm_value = 0;
rgb_yellow();
flag_alarm_set=1;
last_time_button_choose = time_button_choose;
}
else if( ((flag_alarm_set && flag_alarm == 0) || (number == 0 && function_clock == 0)) && (flag_begin != 1))
{
flag_alarm = 1;
flag_alarm_set = 0;
eeprom_write_int(0,number);
alarm_value = eeprom_read_int(0);
flag_alarm_set_ok = 1;
EEPROM.write(3,flag_alarm_set_ok);
function_clock = 1;
waiting_time_cnt=104;
last_time_button_choose = time_button_choose;
}
else if( flag_alarm_set != 1)
{
clock_to_timer();
rgb_light();
if(flag_begin)
{
waiting_time_cnt=104;
}
flag_begin=!flag_begin;
last_time_button_choose = time_button_choose;
}
}
}
void clock_to_timer()
{
if(function_clock)
{
function_clock=0;
number=0;
}
}
void button_display_change_interrupt()
{
uint8_t display_mode[]={0,1,2,3,4};
time_button_display_change = millis();
if (ELIMINAT_THE_JITTER_OF_BUTTON_DISPLAY_CHANGE)
{
switch(display_mode[press_cnt++])
{
case DISPLAY_YEAR:
display_year();
break;
case DISPLAY_MONTH:
display_month();
break;
case DISPLAY_TIME:
display_time();
break;
case DISPLAY_TEMPERATURE:
display_temperature();
break;
case DISPLAY_HUNMIDITY:
display_humidity();
break;
}
if(press_cnt>4)
press_cnt=0;
waiting_time_cnt=1;
}
}
void display_year()
{
flag_year = 1 ;
flag_month = 0;
flag_temperature=0;
flag_humidity=0;
}
void display_month()
{
flag_year = 0 ;
flag_month = 1;
flag_temperature=0;
flag_humidity=0;
}
void display_time()
{
flag_year = 0;
flag_month = 0;
flag_temperature=0;
flag_humidity=0;
function_clock=1;
}
void display_temperature()
{
flag_year =0 ;
flag_month = 0;
flag_temperature=1;
flag_humidity=0;
}
void display_humidity()
{
flag_year = 0;
flag_month = 0;
flag_temperature=0;
flag_humidity=1;
}
void display_clear()
{
flag_year = 0;
flag_month = 0;
flag_temperature=0;
flag_humidity=0;
function_clock=0;
}
MyTimer/buzzer.ino
void buzzer_init(const uint8_t buzzer)
{
pinMode(buzzer,OUTPUT);
}
void time_out()
{
while(flag_begin && number==0)
time_out_effect();
while(alarm_value == dt.hour*100+dt.minute)
time_out_effect();
}
void time_out_effect()
{
if(alarm_value == dt.hour*100+dt.minute)
{
flag_alarm_ring = 1;
flag_alarm_set = 0;
flag_alarm_set_ok = 0;
EEPROM.write(3,flag_alarm_set_ok);
}
clear_display();
rgb_color();
digitalWrite(BUZZER,HIGH);
rgb_color();
digitalWrite(BUZZER,LOW);
rgb_color();
rgb_light();
}
MyTimer/clock.h
#ifndef CLOCK_H
#define CLOCK_H
#include <Wire.h>
struct RTCDateTime
{
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint8_t dayOfWeek;
uint32_t unixtime;
};
RTCDateTime dt;
static uint8_t error_cnt;
#define DS3231_ADDRESS (0x68)
#define DS3231_REG_TIME (0x00)
#define DS3231_REG_CONTROL (0x0E)
const uint8_t daysArray [] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 };
const uint8_t dowArray[] PROGMEM = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
#endif
MyTimer/clock.ino
#include "clock.h"
void clock_init()
{
clock_begin();
}
void update_clock()
{
if(flag_100ms_update_clock)
{dt = clock_getDateTime();}
}
bool clock_begin(void)
{
Wire.begin();
clock_setBattery(true, false);
dt.year = 2000;
dt.month = 1;
dt.day = 1;
dt.hour = 0;
dt.minute = 0;
dt.second = 0;
dt.dayOfWeek = 6;
dt.unixtime = 946681200;
return true;
}
void clock_setBattery(bool timeBattery, bool squareBattery)
{
uint8_t value;
value = clock_readRegister8(DS3231_REG_CONTROL);
if (squareBattery)
{
value |= 0b01000000;
} else
{
value &= 0b10111111;
}
if (timeBattery)
{
value &= 0b01111011;
} else
{
value |= 0b10000000;
}
clock_writeRegister8(DS3231_REG_CONTROL, value);
}
void clock_writeRegister8(uint8_t reg, uint8_t value)
{
Wire.beginTransmission(DS3231_ADDRESS);
#if ARDUINO >= 100
Wire.write(reg);
Wire.write(value);
#else
Wire.send(reg);
Wire.send(value);
#endif
Wire.endTransmission();
}
RTCDateTime clock_getDateTime(void)
{
int values[7];
Wire.beginTransmission(DS3231_ADDRESS);
#if ARDUINO >= 100
Wire.write(DS3231_REG_TIME);
#else
Wire.send(DS3231_REG_TIME);
#endif
Wire.endTransmission();
Wire.requestFrom(DS3231_ADDRESS, 7);
while(!Wire.available())
{
delay(1);
error_cnt++;
if(error_cnt>15)
{
error_cnt=0;
return dt;}};
for (int i = 6; i >= 0; i--)
{
#if ARDUINO >= 100
values[i] = bcd2dec(Wire.read());
#else
values[i] = bcd2dec(Wire.receive());
#endif
}
Wire.endTransmission();
dt.year = values[0] + 2000;
dt.month = values[1];
dt.day = values[2];
dt.dayOfWeek = values[3];
dt.hour = values[4];
dt.minute = values[5];
dt.second = values[6];
dt.unixtime = unixtime();
return dt;
}
uint8_t clock_readRegister8(uint8_t reg)
{
uint8_t value;
Wire.beginTransmission(DS3231_ADDRESS);
#if ARDUINO >= 100
Wire.write(reg);
#else
Wire.send(reg);
#endif
Wire.endTransmission();
Wire.requestFrom(DS3231_ADDRESS, 1);
while(!Wire.available()) {
delay(1);
error_cnt++;
if(error_cnt>15)
{
error_cnt=0;
return value;}};
#if ARDUINO >= 100
value = Wire.read();
#else
value = Wire.receive();
#endif
Wire.endTransmission();
return value;
}
uint8_t bcd2dec(uint8_t bcd)
{
return ((bcd / 16) * 10) + (bcd % 16);
}
uint8_t dec2bcd(uint8_t dec)
{
return ((dec / 10) * 16) + (dec % 10);
}
uint32_t unixtime(void)
{
uint32_t u;
u = time2long(date2days(dt.year, dt.month, dt.day), dt.hour, dt.minute, dt.second);
u += 946681200;
return u;
}
uint16_t date2days(uint16_t year, uint8_t month, uint8_t day)
{
year = year - 2000;
uint16_t days16 = day;
for (uint8_t i = 1; i < month; ++i)
{
days16 += pgm_read_byte(daysArray + i - 1);
}
if ((month == 2) && isLeapYear(year))
{
++days16;
}
return days16 + 365 * year + (year + 3) / 4 - 1;
}
long time2long(uint16_t days, uint8_t hours, uint8_t minutes, uint8_t seconds)
{
return ((days * 24L + hours) * 60 + minutes) * 60 + seconds;
}
bool isLeapYear(uint16_t year)
{
return (year % 4 == 0);
}
void clock_setDateTime(const char* date, const char* time)
{
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
year = conv2d(date + 9);
switch (date[0])
{
case 'J': month = date[1] == 'a' ? 1 : month = date[2] == 'n' ? 6 : 7; break;
case 'F': month = 2; break;
case 'A': month = date[2] == 'r' ? 4 : 8; break;
case 'M': month = date[2] == 'r' ? 3 : 5; break;
case 'S': month = 9; break;
case 'O': month = 10; break;
case 'N': month = 11; break;
case 'D': month = 12; break;
}
day = conv2d(date + 4);
hour = conv2d(time);
minute = conv2d(time + 3);
second = conv2d(time + 6);
setDateTime(year+2000, month, day, hour, minute, second);
}
uint8_t conv2d(const char* p)
{
uint8_t v = 0;
if ('0' <= *p && *p <= '9')
{
v = *p - '0';
}
return 10 * v + *++p - '0';
}
void setDateTime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second)
{
Wire.beginTransmission(DS3231_ADDRESS);
#if ARDUINO >= 100
Wire.write(DS3231_REG_TIME);Serial.println("wwwwwwwwwwwww");
#else
Wire.send(DS3231_REG_TIME);Serial.println("bbbbbbbbbbb");
#endif
#if ARDUINO >= 100
Wire.write(dec2bcd(second));
Wire.write(dec2bcd(minute));
Wire.write(dec2bcd(hour));
Wire.write(dec2bcd(dow(year, month, day)));
Wire.write(dec2bcd(day));
Wire.write(dec2bcd(month));
Wire.write(dec2bcd(year-2000));
#else
Wire.send(dec2bcd(second));
Wire.send(dec2bcd(minute));
Wire.send(dec2bcd(hour));
Wire.send(dec2bcd(dow(year, month, day)));
Wire.send(dec2bcd(day));
Wire.send(dec2bcd(month));
Wire.send(dec2bcd(year-2000));
#endif
#if ARDUINO >= 100
Wire.write(DS3231_REG_TIME);
#else
Wire.send(DS3231_REG_TIME);
#endif
Wire.endTransmission();
}
uint8_t dow(uint16_t y, uint8_t m, uint8_t d)
{
uint8_t dow;
y -= m < 3;
dow = ((y + y/4 - y/100 + y/400 + pgm_read_byte(dowArray+(m-1)) + d) % 7);
if (dow == 0)
{
return 7;
}
return dow;
}
MyTimer/DHT11.ino
static bool measure_environment( float *temperature, float *humidity )
{
static unsigned long measurement_timestamp = millis( );
if( millis( ) - measurement_timestamp > 3000ul )
{
if( dht_sensor.measure( temperature, humidity ) == true )
{
measurement_timestamp = millis( );
return( true );
}
}
return( false );
}
void update_dht11_value()
{
float tem_temperature;
float tem_humidity;
if( measure_environment( &tem_temperature, &tem_humidity ) == true )
{
temperature = tem_temperature;
humidity = tem_humidity;
}
}
MyTimer/eeprom.ino
void eeprom_write_int(unsigned int address, unsigned int data)
{
unsigned int dataL = data&0xFF;
unsigned int dataH = data>>8;
EEPROM.write(address, dataL);
EEPROM.write(address+1, dataH);
}
unsigned int eeprom_read_int(unsigned int address)
{
unsigned int dataL = EEPROM.read(address);
unsigned int dataH = EEPROM.read(address+1);
return((dataH<<8) + dataL);
}
void eeprom_clear_all(unsigned int eeprom_size)
{
unsigned int i = 0;
unsigned char data = 0;
for (i=0; i<eeprom_size; i++)
{
EEPROM.write(i, data);
}
}
MyTimer/MCU_timer.ino
void MCU_timer_interrupt_init(unsigned long time2)
{
MsTimer2::set(time2, timer2_interrupt); // 100ms period
MsTimer2::start();
}
void timer2_interrupt()
{
task1_press_continuously(); //task1 was run every 100 ms. The "100" is because "task1_cnt * TIMER_TIME = 2*50 = 100"
task2_cout_down(); //task2 was run every about 1s(1050ms,Because there may be errors.). The "1050" is because "task2_cnt * TIMER_TIME = 50*21 = 1050"
task3_update_RTCtime(); //task3 was run every about 200 ms. The "200" is because "task3_cnt * TIMER_TIME = 4*50 = 200"
task_reset(); //reset was run every about 5s(5250ms). The "5250" is because "waiting_time_cnt * TIMER_TIME = 105*50 = 5250"
task1_cnt++;
task2_cnt++;
task3_cnt++;
}
void task_reset()
{
if(waiting_time_cnt)
{
waiting_time_cnt++;
}
if(waiting_time_cnt==105)
{
rgb_light();
function_clock=1;
flag_year = 0;
flag_month = 0;
press_cnt = 0;
flag_temperature=0;
flag_humidity=0;
waiting_time_cnt=0;
flag_alarm = 1;
flag_alarm_set = 0;
}
}
MyTimer/MySegDispaly.ino
#define TIME 2
void interface_display()
{
if (function_clock)
{
if (flag_year)
{
number = dt.year;
display_num(number);
}
else if (flag_month)
{
number = dt.month * 100 + dt.day;
display_num(number);
}
else if (flag_temperature)
{
number = temperature * 100;
//clear_bit_display();
temperature_display(number);
}
else if (flag_humidity)
{
number = humidity * 100;
//clear_bit_display();
humidity_display(number);
}
else
{
if (!flag_alarm_set)
number = dt.hour * 100 + dt.minute;
display_num(number);
}
}
else
{
display_num(number);
}
}
void latch_init(const uint8_t latch, const uint8_t clock, const uint8_t data)
{
pinMode(latch, OUTPUT);
pinMode(clock, OUTPUT);
pinMode(data, OUTPUT);
}
void com_init(const uint8_t com1 , const uint8_t com2 , const uint8_t com3 , const uint8_t com4 )
{
pinMode(com1, OUTPUT);
pinMode(com2, OUTPUT);
pinMode(com3, OUTPUT);
pinMode(com4, OUTPUT);
}
void display_num(uint16_t num)
{
uint8_t com = 0;
uint8_t polling = 0;
uint8_t bit_num = 0;
uint8_t num_place[4] = {0};
if (num >= 1000 && num <= 9999)
{
polling = 4;
bit_num = 3;
num_place[3] = num / 1000;
num_place[2] = num / 100 % 10;
num_place[1] = num / 10 % 10;
num_place[0] = num % 10;
for (int i = 0; i < 4; i++)
{ clear_bit_display();
choose_com(polling);
polling--;
if (2 == polling && !flag_year)
{
display2(num_place[bit_num--]);
}
else
display(num_place[bit_num--]);
delay(TIME);
}
}
else if (num >= 100 && num < 1000)
{
polling = 3;
bit_num = 2;
num_place[2] = num / 100;
num_place[1] = num / 10 % 10;
num_place[0] = num % 10;
for (int i = 0; i < 3; i++)
{ clear_bit_display();
choose_com(polling);
polling--;
if (2 == polling && !flag_year)
{
display2(num_place[bit_num--]);
}
else
display(num_place[bit_num--]);
delay(TIME);
}
}
else if (num >= 10 && num < 100)
{
polling = 2;
bit_num = 1;
num_place[1] = num / 10;
num_place[0] = num % 10;
if (flag_alarm_set || function_clock)
{
polling = 4;
for (int i = 0; i < 2; i++)
{ clear_bit_display();
choose_com(polling);
polling--;
display(0);
delay(TIME);
}
}
for (int i = 0; i < 2; i++)
{ clear_bit_display();
choose_com(polling);
polling--;
if (2 == polling && !flag_year)
{
display2(num_place[bit_num--]);
}
else
display(num_place[bit_num--]);
delay(TIME);
}
}
else if (num >= 0 && num <= 9)
{
uint8_t com = 0;
uint8_t polling = 0;
uint8_t bit_num = 0;
uint8_t num_place[4] = {0};
if (flag_alarm_set || function_clock)
{
polling = 4;
for (int i = 0; i < 3; i++)
{ clear_bit_display();
choose_com(polling);
polling--;
display(0);
delay(TIME);
}
}
num_place[0] = num % 10;
choose_com(1);
clear_bit_display();
display(num_place[0]);
delay(TIME);
}
else
{
num = 9999;
}
}
void temperature_display(uint16_t num)
{
uint8_t com = 0;
uint8_t bit_num = 0;
uint8_t num_place[4] = {0};
if (num >= 1000 && num <= 9999)
{
bit_num = 3;
num_place[3] = num / 1000;
num_place[2] = num / 100 % 10;
num_place[1] = num / 10 % 10;
clear_bit_display();
choose_com(4);
display(num_place[bit_num--]);
delay(TIME);
clear_bit_display();
choose_com(3);
digitalWrite(LATCH, LOW);
shiftOut(DATA, CLOCK, MSBFIRST, dht11_buf2[num_place[bit_num--]]);
digitalWrite(LATCH, HIGH);
delay(TIME);
clear_bit_display();
choose_com(2);
display(num_place[bit_num--]);
delay(TIME);
clear_bit_display();
choose_com(1);
display(10);
delay(TIME);
}
}
void humidity_display(uint16_t num)
{
uint8_t com = 0;
uint8_t bit_num = 0;
uint8_t num_place[4] = {0};
if (num >= 1000 && num <= 9999)
{
bit_num = 3;
num_place[3] = num / 1000;
num_place[2] = num / 100 % 10;
num_place[1] = num / 10 % 10;
clear_bit_display();
choose_com(4);
display(num_place[bit_num--]);
delay(TIME);
clear_bit_display();
choose_com(3);
digitalWrite(LATCH, LOW);
shiftOut(DATA, CLOCK, MSBFIRST, dht11_buf2[num_place[bit_num--]]);
digitalWrite(LATCH, HIGH);
delay(TIME);
clear_bit_display();
choose_com(2);
display(num_place[bit_num--]);
delay(TIME);
clear_bit_display();
choose_com(1);
display(11);
delay(TIME);
}
}
void display(uint8_t num)
{
//clear_bit_display();
digitalWrite(LATCH, LOW);
shiftOut(DATA, CLOCK, MSBFIRST, num_buf[num]);
digitalWrite(LATCH, HIGH);
//digitalWrite(LATCH,LOW);
}
void display2(uint8_t num)
{
//clear_bit_display();
digitalWrite(LATCH, LOW);
shiftOut(DATA, CLOCK, MSBFIRST, num_buf_1[num]);
digitalWrite(LATCH, HIGH);
//digitalWrite(LATCH,LOW);
}
void choose_com(uint8_t com)
{
switch (com)
{
case 1: choose_com1();
break;
case 2: choose_com2();
break;
case 3: choose_com3();
break;
case 4: choose_com4();
break;
default : break;
}
}
void choose_com1()
{
digitalWrite(COM1, LOW);
digitalWrite(COM2, HIGH);
digitalWrite(COM3, HIGH);
digitalWrite(COM4, HIGH);
}
void choose_com2()
{
digitalWrite(COM1, HIGH);
digitalWrite(COM2, LOW);
digitalWrite(COM3, HIGH);
digitalWrite(COM4, HIGH);
}
void choose_com3()
{
digitalWrite(COM1, HIGH);
digitalWrite(COM2, HIGH);
digitalWrite(COM3, LOW);
digitalWrite(COM4, HIGH);
}
void choose_com4()
{
digitalWrite(COM1, HIGH);
digitalWrite(COM2, HIGH);
digitalWrite(COM3, HIGH);
digitalWrite(COM4, LOW);
}
void close_all_com()
{
digitalWrite(COM1, LOW);
digitalWrite(COM2, HIGH);
digitalWrite(COM3, HIGH);
digitalWrite(COM4, HIGH);
}
void clear_bit_display()
{
digitalWrite(LATCH, LOW);
shiftOut(DATA, CLOCK, MSBFIRST, 0);
digitalWrite(LATCH, HIGH);
digitalWrite(LATCH, LOW);
delay(TIME);
}
void clear_display()
{
for (int i = 0; i < 4; i++)
{
choose_com(i);
digitalWrite(LATCH, LOW);
shiftOut(DATA, CLOCK, MSBFIRST, 0);
digitalWrite(LATCH, HIGH);
digitalWrite(LATCH, LOW);
delay(TIME);
}
}
MyTimer/MyTimer.h
#ifndef MYTIMER_H
#define MYTIMER_H
#include <dht_nonblocking.h>
#include <EEPROM.h>
void time_out();
void clock_to_timer();
unsigned int eeprom_read_int(unsigned int address);
volatile static uint8_t waiting_time_cnt=0;
volatile static int time_button_ms=0;
volatile static int time_button_s=0;
volatile static int time_button_choose=0;
volatile static int time_button_display_change=0;
volatile static int time_timer=0;
volatile static int last_time_button_ms=0;
volatile static int last_time_button_s=0;
volatile static int last_time_button_choose=0;
volatile static int last_time_button_display_change=0;
volatile static uint8_t function_clock=1;
volatile static uint16_t flag_begin=0;
volatile static uint16_t flag_year=0;
volatile static uint16_t flag_month=0;
volatile static uint16_t flag_temperature=0;
volatile static uint16_t flag_humidity=0;
volatile static uint16_t flag_alarm=1;
volatile static uint16_t flag_alarm_set=0;
volatile static uint16_t flag_alarm_set_ok= EEPROM.read(3);
volatile static uint16_t flag_alarm_ring=0;
volatile static uint16_t flag_100ms_update_clock=1;
volatile static float temperature;
volatile static float humidity;
volatile static uint8_t press_cnt;
volatile static uint16_t number=0;
volatile const static uint8_t num_buf[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x39,0x76};
volatile const static uint8_t num_buf_1[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef,0x39,0x76};//.
volatile static uint8_t dht11_buf[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
volatile static uint8_t dht11_buf2[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};
volatile static uint16_t alarm_value = eeprom_read_int(0);
#define DHT_SENSOR_PIN 11
DHT_nonblocking dht_sensor( DHT_SENSOR_PIN, DHT_TYPE_11 );
#define TIMER_TIME 50
#define BUTTON_S A0
#define BUTTON_MS A2
#define BUTTON_CHOOSE A1
#define BUTTON_DISPLAY_CHANGE 10
//#define COM4 5
//#define COM3 4
//#define COM2 30
//#define COM1 2
#define COM1 2
#define COM2 4
#define COM3 A3
#define COM4 7
#define LATCH 12 //STCP
#define CLOCK 13 //SHCP
#define DATA 8 //DS
#define BUZZER 3
#define GREEN 9
#define RED 6
static const uint8_t rgb_color_delay = 15;
static uint16_t task1_cnt=0;
static uint16_t task2_cnt=0;
static uint16_t task3_cnt=0;
enum display_mode
{
DISPLAY_YEAR,
DISPLAY_MONTH,
DISPLAY_TIME,
DISPLAY_TEMPERATURE,
DISPLAY_HUNMIDITY,
};
#endif
MyTimer/MyTimer.ino
#include "PinChangeInterrupt.h"
#include "MsTimer2.h"
#include "clock.h"
#include "MyTimer.h"
#include "MyTimerDetails.h"
#include <Wire.h>
#include <dht_nonblocking.h>
#include <avr/wdt.h>
#include <EEPROM.h>
/***********************************************************************************************************************
* My Timer *
* Copyright (c) Elegoo //www.elegoo.com
* Author: wee * *
* Version number : 1.00 *
* Date : 2019.8.22
* *
***********************************************************************************************************************/
void setup()
{// ←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←← ←
// ↓ //******************************************************************* ↑
//You_must_do_for_the_first_time(); // if you are the first time to upload the code you must delete the '//'
// to initialize the time .
// And then add the '//' again in the second time.
//*******************************************************************
latch_init(LATCH,CLOCK,DATA); //74hC595
com_init(COM1,COM2,COM3,COM4);
button_init(BUTTON_S,BUTTON_MS,BUTTON_CHOOSE,BUTTON_DISPLAY_CHANGE);
button_interrupt_init(BUTTON_S,BUTTON_MS,BUTTON_CHOOSE,BUTTON_DISPLAY_CHANGE);
MCU_timer_interrupt_init(TIMER_TIME); //set timer2 0.05s
buzzer_init(BUZZER);
clock_init();
rgb_init();
wdt_enable(WDTO_120MS);
rgb_light();
}
void loop()
{
update_clock();
update_dht11_value();
interface_display();
time_out();
wdt_reset();
}
void You_must_do_for_the_first_time()
{
clock_setDateTime(__DATE__, __TIME__);
eeprom_clear_all(256);
}
MyTimer/MyTimerDetails.h
#ifndef MYTIMERDETAILS_H
#define MYTIMERDETAILS_H
#define BUTTON_MS_WAS_PRESSED_AND_LASTS_FOR_1000_MS (!digitalRead(BUTTON_MS) && 1000 < (abs(time_button_ms-time_timer)))
#define BUTTON_S_WAS_PRESSED_AND_LASTS_FOR_1000_MS (!digitalRead(BUTTON_S) && 1000 < (abs(time_button_s-time_timer)))
#define ELIMINAT_THE_JITTER_OF_BUTTON_S (200 < (abs(time_button_s - last_time_button_s)))
#define ELIMINAT_THE_JITTER_OF_BUTTON_MS (200 < (abs(time_button_ms - last_time_button_ms)))
#define ELIMINAT_THE_JITTER_OF_BUTTON_CHOOSE (200 < (abs(time_button_choose - last_time_button_choose)))
#define ELIMINAT_THE_JITTER_OF_BUTTON_DISPLAY_CHANGE (200 < (abs(time_button_display_change - last_time_button_display_change)))
#endif
MyTimer/RGB.ino
void rgb_init()
{
pinMode(RED, OUTPUT);
pinMode(GREEN, OUTPUT);
digitalWrite(RED, LOW);
digitalWrite(GREEN, LOW);
// rgb_green();
}
void rgb_color()
{
int redValue = 20;
int greenValue = 0;
for(int i = 0; i < 20; i += 1)
{
wdt_reset();
redValue -= 1;
greenValue += 1;
analogWrite(RED, redValue);
analogWrite(GREEN, greenValue);
delay(rgb_color_delay);
}
}
void rgb_light()
{
if(flag_alarm_set_ok)
rgb_yellow();
else
rgb_green();
}
void rgb_green()
{
analogWrite(RED, 0);
analogWrite(GREEN, 33);
}
void rgb_yellow()
{
analogWrite(RED, 33);
analogWrite(GREEN, 33);
}
void close_rgb()
{
analogWrite(RED, 0);
analogWrite(GREEN, 0);
}
MyTimer/timer_task1.ino
void task1_press_continuously()
{
if(task1_cnt == 2)
{
task1_cnt = 0;
time_timer=millis();
task1_case1_button_ms_was_pressed_and_lasts_for_1000ms();
task1_case2_button_s_was_pressed_and_lasts_for_1000ms();
}
}
void task1_case1_button_ms_was_pressed_and_lasts_for_1000ms()
{
if(BUTTON_MS_WAS_PRESSED_AND_LASTS_FOR_1000_MS)
{
number +=1;
waiting_time_cnt=1;
if(number%100 == 60 && number != 60)
number = number/100*100+100;
if(flag_alarm_set && number >=2359)
{number = 0;}
if(number==60)
number=100;
}
}
void task1_case2_button_s_was_pressed_and_lasts_for_1000ms()
{
if(BUTTON_S_WAS_PRESSED_AND_LASTS_FOR_1000_MS)
{
number +=100;
waiting_time_cnt=1;
if(flag_alarm_set && number >=2400)
{ number = 0;}
}
}
MyTimer/timer_task2.ino
void task2_cout_down()
{
if(task2_cnt == 21)
{
task2_cnt = 0;
if(flag_begin)
{
if (number > 9999)
number = 9999;
if(number%100 > 60 || number == 99)
number = number/100*100+59;
if(number>0)
{
number--;
waiting_time_cnt=1;
}
if (number > 9999)
number = 9999;
if(number%100 > 60 || number == 99)
number = number/100*100+59;
}
}
}
MyTimer/timer_task3.ino
void task3_update_RTCtime()
{
if(task3_cnt == 100)
{
task3_cnt = 0;
flag_100ms_update_clock=!flag_100ms_update_clock;
}
}
ملفات الدرس والمكتبات
المكتبات المرفقة
ملفات الدرس
- 3Dprint/Epic Amur (29).stl
- 3Dprint/Epic Amur (34).stl
- 4.5 Multifunctional alarm clock.pdf
- MyTimer/button.ino
- MyTimer/buzzer.ino
- MyTimer/clock.h
- MyTimer/clock.ino
- MyTimer/DHT11.ino
- MyTimer/eeprom.ino
- MyTimer/MCU_timer.ino
- MyTimer/MySegDispaly.ino
- MyTimer/MyTimer.h
- MyTimer/MyTimer.ino
- MyTimer/MyTimerDetails.h
- MyTimer/RGB.ino
- MyTimer/timer_task1.ino
- MyTimer/timer_task2.ino
- MyTimer/timer_task3.ino