پیشرفته⏱ 25 دقیقه مطالعه

وقتی هم‌زمان چند کار دارید (مثلاً چشمک زدن LED، خواندن سنسور و پاسخ به درخواست‌های وب)، نوشتن همه در یک loop() ساده به‌سرعت پیچیده می‌شود. در میکروپایتون این مشکل با uasyncio (کتابخانهٔ async/await) حل می‌شد؛ در Arduino-ESP32 معادل واقعی و حتی قدرتمندتر آن FreeRTOS است — سیستم‌عامل بلادرنگی که خودِ هستهٔ Arduino-ESP32 از قبل روی آن سوار شده و اجازه می‌دهد چند «Task» واقعاً به‌صورت زمان‌بندی‌شده (نه فقط ظاهری) هم‌زمان اجرا شوند.

ساخت چند Task با xTaskCreate

CPP
const int LED_PIN = 2;

void blinkTask(void* parameter) {
  pinMode(LED_PIN, OUTPUT);
  while (true) {
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
    vTaskDelay(500 / portTICK_PERIOD_MS);   // معادل await asyncio.sleep_ms(500)
  }
}

void sensorTask(void* parameter) {
  int counter = 0;
  while (true) {
    counter++;
    Serial.print("خواندن شمارهٔ ");
    Serial.println(counter);
    vTaskDelay(2000 / portTICK_PERIOD_MS);
  }
}

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

  // نام Task، اندازهٔ پشته، پارامتر، اولویت، دستهٔ Task
  xTaskCreate(blinkTask,  "Blink",  2048, NULL, 1, NULL);
  xTaskCreate(sensorTask, "Sensor", 2048, NULL, 1, NULL);
}

void loop() {
  // خودِ loop() هم در واقع یک Task دیگر از FreeRTOS است؛
  // می‌تواند خالی بماند یا کار سبک دیگری انجام دهد
  vTaskDelay(1000 / portTICK_PERIOD_MS);
}
ℹ️ اطلاعات

نکتهٔ مهم: به‌جای delay() معمولی که کل هستهٔ پردازنده را مسدود می‌کند، در داخل یک Task از vTaskDelay() استفاده می‌کنیم تا زمان‌بند FreeRTOS بتواند در همان لحظه Taskهای دیگر را اجرا کند — دقیقاً همان نقشی که await asyncio.sleep() در uasyncio بازی می‌کرد.

شبیه‌سازی زندهٔ FreeRTOS — دو Task مستقل، هر کدام یک LED را با vTaskDelay و سرعت متفاوت چشمک می‌زنند (روی پایه‌های ۲ و ۴) باز کردن در Wokwi ↗
در حال بارگذاری شبیه‌ساز… (ممکن است چند ثانیه طول بکشد)

این یک شبیه‌سازی واقعی و قابل اجراست؛ روی دکمه پخش (▶) در Wokwi بزنید تا کد روی برد مجازی ESP32 اجرا شود. اگر جعبهٔ بالا بیش از حد سیاه/خالی ماند، صفحه را رفرش کنید یا از دکمهٔ «باز کردن در Wokwi» بالا استفاده کنید.

پروژهٔ پایانی: پنل کوچک خانهٔ هوشمند

در این پروژهٔ جمع‌بندی، مفاهیم فصل‌های قبل را ترکیب می‌کنیم: خواندن سنسور DHT22 (فصل ۳)، نمایش وضعیت روی OLED (فصل ۴)، و اجرای هم‌زمان وظایف با FreeRTOS Tasks.

CPP
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <DHT.h>

#define DHT_PIN 15
DHT dht(DHT_PIN, DHT22);
Adafruit_SSD1306 display(128, 64, &Wire, -1);

void readAndDisplayTask(void* parameter) {
  while (true) {
    float t = dht.readTemperature();
    float h = dht.readHumidity();
    if (!isnan(t) && !isnan(h)) {
      display.clearDisplay();
      display.setCursor(0, 10);
      display.print("Dama: ");
      display.print(t);
      display.println("C");
      display.setCursor(0, 30);
      display.print("Rotoobat: ");
      display.print(h);
      display.println("%");
      display.display();
    }
    vTaskDelay(2000 / portTICK_PERIOD_MS);
  }
}

void setup() {
  Wire.begin(21, 22);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  dht.begin();

  xTaskCreate(readAndDisplayTask, "ReadDisplay", 4096, NULL, 1, NULL);
}

void loop() {
  vTaskDelay(1000 / portTICK_PERIOD_MS);
}
🧩

برای پروژهٔ پایانی، نمایشگر OLED (I2C روی SDA=21, SCL=22) و سنسور DHT22 (پایهٔ ۱۵) را در یک پروژهٔ خالی Arduino/ESP32 در Wokwi اضافه کنید.

ساخت پروژهٔ خالی Arduino/ESP32 در Wokwi ↗
🛠️ کارگاه عملی

پروژهٔ پایانی: کد بالا را با بخش Wi-Fi/MQTT درس ۱۳ ترکیب کنید تا یک Task سوم (sendToCloudTask) هر ۱۰ ثانیه دادهٔ آخرین اندازه‌گیری را به بروکر MQTT ارسال کند؛ هر سه Task باید هم‌زمان و بدون توقف یکدیگر اجرا شوند.

❓ در یک FreeRTOS Task، به‌جای delay() معمولی از چه دستوری استفاده می‌کنیم تا Taskهای دیگر مسدود نشوند؟
طراحی و توسعه: امین شیخ نجدیglcd.ir | نسخهٔ افزونه: 1.21.0