سلام. آیا این امکان وجود دارد که در آردینو یک دستور را به صورت خودکار مثلا هر ۶ ساعت یکبار اجرا کرد؟ اگر امکان داره لطفا دستوراتش رو بفرمایید.
از چند نفر پرسیدم گفتن این امکان وجود نداره و در سایر بخشهای برنامه ایجاد مشکل میکنه.
سلام. آیا این امکان وجود دارد که در آردینو یک دستور را به صورت خودکار مثلا هر ۶ ساعت یکبار اجرا کرد؟ اگر امکان داره لطفا دستوراتش رو بفرمایید.
از چند نفر پرسیدم گفتن این امکان وجود نداره و در سایر بخشهای برنامه ایجاد مشکل میکنه.
با سلام.
اگر مصرف برق برای کاربرد شما اهمیت دارد، می توانید از ESP8266 یا ESP32 استفاده کنید و آن را به مدت دلخواه خود به حالت Sleep ببرید تا وقتی که از این حالت خارج شد، دستورات شما را اجرا کنید. حدوداً تا 1 ساعت می تواند به حالت sleep برود. هر ساعت یکبار که از این حالت خارج شد، به یک شمارنده، 1 عدد اضافه کنید و وقتی شماره شما برابر 6 شد، کد مشخص خود را اجرا کنید.
اگر این موضوع اهمیت ندارد، می توانید از بردهای آردوینو به همراه ماژولهای تایمر و ساعت استفاده کنید. هر لحظه ساعت را چک کنید و پس از گذشت مدت دلخواه، کد خود را اجرا کنید.
اگر ماژولی را پیدا کنید که خروجی وقفه (Interrupt) داشته باشد، نیاز به چک کردن ساعت وجود نخواهید داشت و فقط در تابع وقفه، کد مورد نظر خود را بنویسید.
بله، میتوانید در آردوینو یک دستور را به صورت خودکار و با فاصله زمانی مشخص اجرا کنید. برای این کار میتوانید از تایمرها و توابع زمانسنجی در آردوینو استفاده کنید.
یک راه بسیار ساده برای اجرای یک دستور هر 6 ساعت، استفاده از توابع `millis()` و متغیرهای زمانی است. متغیرهای زمانی به شما اجازه میدهند زمان گذشته شده را بر اساس مقدار بازگشتی `millis()` محاسبه کنید.
در زیر یک نمونه کد آردوینو برای اجرای یک دستور هر 6 ساعت آورده شده است:
unsigned long previousMillis = 0; // متغیری برای ذخیره زمان سپری شده
// 6 * 60 * 60 * 1000 = 21600000 // محاسبه مدت زمان مورد نظر بر حسب میلیثانیه (6 ساعت در مورد سوال شما برحسب میلی ثانیه محاسبه شده)
const unsigned long interval = 21600000;
void setup()
{
// کد مربوط به تنظیمات اولیه
}
void loop() {
unsigned long currentMillis = millis(); // زمان فعلی را دریافت میکنیم
if (currentMillis - previousMillis >= interval) // مدت زمان سپری شده را بررسی میکنیم
{
previousMillis = currentMillis; // زمان سپری شده را به روز میکنیم
// دستور مورد نظر اجرا شود
// این قسمت را با دستور مورد نظر خود جایگزین کنید
// مثال: digitalWrite(13, HIGH);
}
// کد بقیه برنامه
}
در این کد، متغیر `previousMillis` برای ذخیره زمان گذشته شده استفاده میشود. هر بار که زمان گذشته شده از فاصله زمانی تعیین شده (`interval`) بیشتر یا مساوی شود، دستور مورد نظر در قسمت مشخص شده اجرا میشود و متغیر `previousMillis` به روز رسانی میشود.
در این نمونه، فاصله زمانی 6 ساعت را با استفاده از ضرب در 60 (دقیقه) و 60 (ثانیه) و 1000 (میلیثانیه) به میلیثانیه تعیین کردهایم. شما میتوانید این مقدار را به طور مستقیم تغییر دهید تا فاصله زمانی مورد نظر خود را تنظیم کنید.
لازم به ذکر است که اگر در طول اجرای دستورات دیگری نیاز به استفاده از توابع `delay()` داشته باشید، بهتر است از توابع غیرمسدودکننده مانند `millis()` و `micros()` استفاده کنید.
خواهش می کنم.
ببینید وقتی millis بعد از 49 روز یا اگر دقیقش رو بخواهیم بگیم و ۴۹.۷۱ روز (به عبارت دقیق تر هر ۲ به توان ۳۲ میلی ثانیه) - یعنی حدوداً نزدیک به 50 روز - سر ریز میشه یعنی مقدار millis صفر میشه.
unsigned long previousMillis = 0; // متغیری برای ذخیره زمان سپری شده
// محاسبه مدت زمان مورد نظر بر حسب میلیثانیه (6 ساعت در مورد سوال شما برحسب میلی ثانیه محاسبه شده):
const unsigned long interval =21600000; // 6 * 60 * 60 * 1000;
void setup()
{
// کد مربوط به تنظیمات اولیه
}
void loop()
{
unsigned long currentMillis = millis(); // زمان فعلی را دریافت میکنیم. این عدد هر 50 روز دوباره صفر میشه
if (currentMillis - previousMillis >= interval) // مدت زمان سپری شده را بررسی میکنیم
{
previousMillis = currentMillis; // زمان سپری شده را به روز میکنیم
// دستور مورد نظر اجرا شود
// این قسمت را با دستور مورد نظر خود جایگزین کنید
// مثال: digitalWrite(13, HIGH);
}
// کد بقیه برنامه
}
خوب من برنامه رو بدون else تست کردم و از اونجایی که نمیتونیم 50 روز صبر کنیم متغیرهای زمانی و شرایط سرریز شدن millis رو شبیه سازی کردم و متغیرهای زمان رو مانیتور کردم. خوشبختانه نیاز به قسمت else نداریم و برنامه بعد از سرریز هم درست به کارش ادامه میده . پس کد زیر بدون مشکل کار میکنه:
unsigned long previousMillis = 0; // متغیری برای ذخیره زمان سپری شده
// محاسبه مدت زمان مورد نظر بر حسب میلیثانیه (6 ساعت در مورد سوال شما برحسب میلی ثانیه محاسبه شده):
const unsigned long interval = 21600000; //6 * 60 * 60 * 1000;
void setup()
{
// کد مربوط به تنظیمات اولیه
}
void loop()
{
unsigned long currentMillis = millis(); // زمان فعلی را دریافت میکنیم. این عدد هر 50 روز دوباره صفر میشه
if (currentMillis - previousMillis >= interval) // مدت زمان سپری شده را بررسی میکنیم
{
previousMillis = currentMillis; // زمان سپری شده را به روز میکنیم
// دستور مورد نظر اجرا شود
// این قسمت را با دستور مورد نظر خود جایگزین کنید
// مثال: digitalWrite(13, HIGH);
}
// کد بقیه برنامه
}
به تفاوت اعداد مشخص شده دقت کنید. اینجا تابع micros سرریز شده و برنامه بدون هیچ اختلالی داره به کارش ادامه میده.
این هم کدی که برای تست تغییر دادم . سرریزش 70 دقیقه است:
یک LED به pin13 وصل کنید، پنجره serial رو باز کنید و برنامه رو اجرا کنید در ابتدا LED خاموشه ولی بعد از 5 دقیقه روشن شده و هر 5 دقیقه خاموش یا روشن میشه هربار به عدد counter در پنجره serial دقت کنید. تعداد دفعات اجرای برنامه مورد نظر رو نشون میده وقتی این عدد از 14 بیشتر شد یعنی تابع سرریز شده ولی برنامه بدون مشکل به کارش ادامه میده.
const int ledPin = LED_BUILTIN; // the number of the LED pin: پین 13
// Variables will change:
int ledState = LOW; // ledState used to set the LED
unsigned long previousMillis = 0; // متغیری برای ذخیره زمان سپری شده
// محاسبه مدت زمان مورد نظر بر حسب میلیثانیه (6 ساعت در مورد سوال شما برحسب میلی ثانیه محاسبه شده):
// 6 * 60 * 60 * 1000 = 21600000
// حتما نتیجه ضرب بالا رو در متغیر ذخیره کنید مثل خط زیر:
//const unsigned long interval = 21600000;
//test: ///////////////// محاسبه مدت زمان مورد نظر بر حسب میکرو ثانیه (5 دقیقه برای تست بجای 6 ساعت برحسب میکرو ثانیه محاسبه شده):
const unsigned long interval =3000000; // 1000000 * 60 * 5 = 5 minuet
////////////////////////
void setup() {
// کد مربوط به تنظیمات اولیه
Serial.begin(9600);
Serial.println("Start: ");
}
int counter = 0;
void loop() {
//unsigned long currentMillis = millis(); // زمان فعلی را دریافت میکنیم. این عدد هر 50 روز دوباره صفر میشه
//test: ///////////////// این قسمت برای تست تغییر داده شده
// زمان فعلی را دریافت میکنیم. این عدد هر 70 دقیقه صفر میشه
unsigned long currentMillis = micros(); // millis برای تست به جای
/////////////////////////
// مدت زمان سپری شده را بررسی میکنیم
if (currentMillis - previousMillis >= interval)
{
counter++;
Serial.print(currentMillis - previousMillis);
Serial.print(" currentMillis: ");Serial.print(currentMillis);
Serial.print(" Prev: "); Serial.print(previousMillis);
Serial.print(" Counter: "); Serial.print(counter); Serial.println();
previousMillis = currentMillis; // زمان سپری شده را به روز میکنیم
// دستور مورد نظر اجرا شود
// این قسمت را با دستور مورد نظر خود جایگزین کنید
// مثال: digitalWrite(13, HIGH);
// if the LED is off turn it on and vice-versa:
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
// کد بقیه برنامه
// کدهایی که اینجا می نویسید بدون مزاحمت و بدون تاخیر، مستقلا اجرا میشن
}
در روش فوق برای انجام چند کار به صورت همزمان نباید از توابع تاخیری مثل delay استفاده کنیم چون تمام زمانبندی ها رو بهم میریزه. یعنی زمانی که مثلا 10 ثانیه توقف برای روشن ماندن LED با Delay ایجاد میکنیم ممکنه از زمان در حال شمارش Milies قافل بشیم و کاری که براش زمانبندی کردیم با 10ئ ثانیه تاخیر انجام بشه.
راه حل1: با فرض اینکه قرار است برنامه شما مثلا هر شش ساعت یکبار کار خاصی را انجام بدهد. اگر در ضمن آن پروسه شش ساعت یکبار بخواهید کنترل های دیگری هم انجام بدهید مانند همین مثالی که فرمودید با فشردن کلید بدون استفاده از توابع تاخیری مثل delay، یک ال ای دی به مدت ۱۰ ثانیه روشن بماند و بعد خاموش شود. خوب تابع miles رو داریم که داره کارش رو انجام میده.
فرض کنید تابع miles یک ساعت دیواری هست که میکروکنترلر مرتب بهش نگاه میکنه و بعد از گذشت ۶ ساعت کاری که ازش خواستیم رو انجام میده. پس در این بین میتونیم کارهای دیگه هم ازش بخواهیم که سر وقتش انجام بده. یعنی نیازی نیست دوبار milies رو برای هر کاری از اول فراخوانی کنیم. ما ساعت دیواری رو داریم کافیه برای انجام هرکاری فقط بهش نگاه کنیم، اگر زمانش فرارسیده بود اون کار رو انجام بدیم.
مثلا با فشردن کلید مقدار miles رو میخونیم و در یک متغیر ذخیره میکنیم و پایه 3 رو هم HIGH میکنیم تا ال ای دی روشن بشه . حالا با یک شرط مثل همونی که زمان شش ساعته رو کنترل میکنه مرتب زمان فعلی رو در loop میخوانیم و مقدار ذخیره شده قبلی رو ازش کم میکنیم اگر این مقدار بزرگتر یا مساوی 10 ثانیه شد پایه 3 رو LOW میکنیم تا ال ای دی خاموش بشه. اینطوری میتونیم چندتا کار رو همزمان بدون استفاده از delay انجام بدیم.
راه حل2: یک روش دیگه هم میتونم پیشنهاد بدم و اون استفاده از بردهای دوهسته ای مثل ESP32 و Raspberry Pi Pico هست که برای هر هسته جداگانه میتونید برنامه ریزی کنید و حتی از دستور delay هم میتونید استفاده کنید. هر هسته جداگانه کار خودش رو انجام میده و مزاحمتی برای کار هسته دیگه نداره. ضمن اینکه این بردها دارای حافظه بیشتر و سرعت پردازش بسیار بالاتری هستند و تکنولوژی اونها به روز است و جذابیت خیلی بیشتری نسبت به میکرو های 8 بیتی دارند.
پیشنهاد دیگه اینکه اگر بیش از دو کار مجزا رو میخواهید به صورت همزمان برنامه ریزی کنید از برد Arduino Due استفاده کنید. البته بورد های ESP32 و Raspberry Pi Pico با اینکه از آردوینو uno پیشرفته تر و قویتر هستند از اون ارزانتر هم هستند ولی Arduino Due گران تر است.