ITcoDe.Uz ← Maqolalar

Arduino

delay() o'rniga millis(): bloklamaydigan tugma bosish mantiqi

Uzun bosishni delay(3300) bilan yozish oson. Lekin shu uch soniya davomida qurilma butunlay o'lik bo'ladi: MQTT uziladi, GPS o'qilmaydi, hech qanday buyruq qabul qilinmaydi.

Bakhrullo Nasrulloyev2026-08-14~7 daqiqa

Sodda yechim va uning narxi

Push-start moduliga uzun signal berish kerak bo'lganda birinchi ko'ringan yechim shunday bo'ladi:

void pressLong(uint8_t pin) {
  digitalWrite(pin, HIGH);
  delay(3300);          // ← muammo shu yerda
  digitalWrite(pin, LOW);
}

Bu ishlaydi. Lekin delay() protsessorni to'liq to'xtatadi. Uch soniya davomida:

Amaliy oqibat. Menda avtomobil o't oldirgandan keyin telefon "qurilma javob bermayapti" deb ko'rsatardi. Buyruq bajarilgan, lekin holat qaytmagan — chunki o'sha paytda MQTT uzilgan edi.

Asosiy g'oya

Kutish o'rniga vaqtni belgilab qo'yish kerak. Ya'ni: "hozir tugmani bosdim, uni 3300 millisekunddan keyin qo'yib yuboraman" deb yozib qo'yamiz va darhol boshqa ishlarga qaytamiz.

Har loop() aylanishida shunchaki tekshiramiz: vaqt keldimi? Kelgan bo'lsa — pinni tushiramiz.

Amalga oshirish

Uchta o'zgaruvchi yetarli: qaysi pin bosilgan, qachon bosilgan, qancha ushlanishi kerak.

struct Press {
  uint8_t  pin      = 255;   // 255 = bo'sh
  uint32_t startMs  = 0;
  uint16_t holdMs   = 0;
};

Press active;

bool pressBegin(uint8_t pin, uint16_t holdMs) {
  if (active.pin != 255) return false;  // band
  active.pin     = pin;
  active.startMs = millis();
  active.holdMs  = holdMs;
  digitalWrite(pin, HIGH);
  return true;
}

void pressUpdate() {
  if (active.pin == 255) return;
  if (millis() - active.startMs >= active.holdMs) {
    digitalWrite(active.pin, LOW);
    active.pin = 255;
  }
}

Endi asosiy tsikl toza va uzluksiz ishlaydi:

void loop() {
  mqtt.loop();       // aloqa uzilmaydi
  gpsRead();         // koordinata yangilanadi
  sensorsUpdate();   // harorat o'qiladi
  pressUpdate();     // tugma o'z vaqtida qo'yib yuboriladi
}

Nima uchun ayirma, taqqoslash emas

E'tibor bering, shart shunday yozilgan: millis() - startMs >= holdMs. Buni millis() >= startMs + holdMs deb yozish mumkindek tuyuladi, lekin bu xato.

millis() taxminan 49 kundan keyin nolga qaytadi. Agar qurilma o'sha paytda tugmani bosayotgan bo'lsa, ikkinchi variant abadiy kutishga tushadi. Birinchi variant esa to'g'ri ishlaydi, chunki belgilanmagan butun sonlar ayirmasi to'lib ketishni o'zi hisobga oladi.

Band holatni qaytarish

Funksiya false qaytarganda buyruq bajarilmagan bo'ladi. Buni foydalanuvchiga bildirish kerak, aks holda u tugmani qayta-qayta bosaveradi:

if (!pressBegin(PIN_START, T_LONG)) {
  publishState("busy");   // telefon "kuting" deb ko'rsatadi
}

Bu bir vaqtda ikkita buyruq bajarilishining oldini oladi. Avtomobilda bu muhim — "qulfla" va "ochish" bir vaqtda ishga tushsa, modul noaniq holatga tushishi mumkin.

Xuddi shu naqsh boshqa joylarda

Bloklamaydigan yondashuv faqat tugmalar uchun emas. Men uni bir nechta joyda ishlataman:

VazifaBloklaydigan usulBloklamaydigan usul
Harorat o'qishdelay(750)So'rov yuborib, keyin o'qish
Holat yuborishHar tsiklda5 soniyada bir marta
Qayta ulanishdelay(5000)Vaqt belgisi bilan
Svetodiod miltillashiIkki delay()Interval tekshiruvi

DS18B20 harorat datchigi yaxshi misol. U o'lchash uchun 750 millisekund vaqt talab qiladi. Kutish o'rniga: so'rov yuboriladi, vaqt belgilanadi, keyingi tsikllarda boshqa ishlar bajariladi va vaqt kelganda natija o'qiladi.

Qachon delay() joiz

Har doim ham uni tashlash shart emas. delay() quyidagi hollarda muammo tug'dirmaydi:

Lekin tarmoqqa ulangan qurilmada asosiy tsikl ichida uzun delay() — bu deyarli har doim keyinchalik tushunarsiz xatolarga aylanadi.

Xulosa

State machine yondashuvi kodni biroz uzunroq qiladi, lekin qurilmani tirik saqlaydi. NBcaR da bu o'zgarishdan keyin MQTT uzilishlari butunlay to'xtadi va holat telefonda darhol yangilanadigan bo'ldi.

Qoida: asosiy tsikl hech qachon to'xtamasin. Kutish kerak bo'lsa — vaqtni belgilang, kutmang.

ArduinoESP32millisState machineEmbedded