MINISCHEDULER pro Attiny84/85

Odpovědět
Uživatelský avatar
SkullKeep
Příspěvky: 316
Registrován: 23 srp 2017, 18:51
Bydliště: Brno
Kontaktovat uživatele:

MINISCHEDULER pro Attiny84/85

Příspěvek od SkullKeep » 14 zář 2025, 14:55

Všichni znáte <TaskScheduler.h> na git hubu. A proto jsem si necha napsat MiniScheduler pro Attiny84/85 jednoduchý a přitom nesebere moc paměti. Chápu lidi moc Attiny84/85 nemusí, ale je malý a toto se hodí.
Vlastnosti:
Paměťová stopa: pro 5 úloh ~80 B SRAM.
Bez dynamiky → vhodné pro malé ATtiny.
Jednoduché API: addTask, enableTask, disableTask, run.

Kód: Vybrat vše

#include <MiniScheduler.h>

MiniScheduler scheduler;

void blinkLed() {
  digitalWrite(0, !digitalRead(0));
}

void sayHello() {
  digitalWrite(1, HIGH); // rozsvítí LED jen jednou
}

void setup() {
  pinMode(0, OUTPUT);
  pinMode(1, OUTPUT);

  scheduler.addTask(blinkLed, 500, true);   // opakovaná úloha
  scheduler.addTask(sayHello, 2000, false); // jednorázová úloha
}

void loop() {
  scheduler.run();
}
zabere

Kód: Vybrat vše

Sketch uses 936 bytes (11%) of program storage space. Maximum is 8192 bytes.
Global variables use 70 bytes (13%) of dynamic memory, leaving 442 bytes for local variables. Maximum is 512 bytes.
Vlastní knihovna:
MiniScheduler.h

Kód: Vybrat vše

#ifndef MINISCHEDULER_H
#define MINISCHEDULER_H

#include <Arduino.h>

#define MAX_TASKS 5   // maximální počet úloh

typedef void (*TaskCallback)(); // ukazatel na funkci

struct MiniTask {
  TaskCallback callback;
  unsigned long interval;
  unsigned long lastRun;
  bool enabled;
  bool repeat; // zda se má spouštět opakovaně
};

class MiniScheduler {
  public:
    MiniScheduler();
    bool addTask(TaskCallback cb, unsigned long interval, bool repeat = true);
    void enableTask(uint8_t id);
    void disableTask(uint8_t id);
    void run(); // volat v loop()
  private:
    MiniTask tasks[MAX_TASKS];
    uint8_t taskCount;
};

#endif

MiniScheduler.cpp

Kód: Vybrat vše

#include "MiniScheduler.h"

MiniScheduler::MiniScheduler() {
  taskCount = 0;
  for (uint8_t i = 0; i < MAX_TASKS; i++) {
    tasks[i].callback = nullptr;
    tasks[i].interval = 0;
    tasks[i].lastRun = 0;
    tasks[i].enabled = false;
    tasks[i].repeat = true;
  }
}

bool MiniScheduler::addTask(TaskCallback cb, unsigned long interval, bool repeat) {
  if (taskCount >= MAX_TASKS) return false;
  tasks[taskCount].callback = cb;
  tasks[taskCount].interval = interval;
  tasks[taskCount].lastRun = millis();
  tasks[taskCount].enabled = true;
  tasks[taskCount].repeat = repeat;
  taskCount++;
  return true;
}

void MiniScheduler::enableTask(uint8_t id) {
  if (id < taskCount) tasks[id].enabled = true;
}

void MiniScheduler::disableTask(uint8_t id) {
  if (id < taskCount) tasks[id].enabled = false;
}

void MiniScheduler::run() {
  unsigned long now = millis();
  for (uint8_t i = 0; i < taskCount; i++) {
    if (tasks[i].enabled && tasks[i].callback) {
      if (now - tasks[i].lastRun >= tasks[i].interval) {
        tasks[i].lastRun = now;
        tasks[i].callback();
        if (!tasks[i].repeat) {
          tasks[i].enabled = false; // vypne se po prvním spuštění
        }
      }
    }
  }
}
keywords.txt

Kód: Vybrat vše

MiniScheduler    KEYWORD1
addTask          KEYWORD2
enableTask       KEYWORD2
disableTask      KEYWORD2
run              KEYWORD2
:twisted: :?: :arrow: :geek: P. Q. M.

AstroMiK
Příspěvky: 652
Registrován: 08 pro 2017, 19:05

Re: MINISCHEDULER pro Attiny84/85

Příspěvek od AstroMiK » 14 zář 2025, 16:44

Na větších projektech ten TaskScheduler smysl určitě má.
Je s tím jednodušší práce a navenek je to i přehlednější (kód je ukytý někde hluboko v knihovně).
Já ale obvykle na malých procesorech ATtiny bojuju o každý bajt a tam raději použiju klasický způsob časování místo objektového.

Pro porovnání jsem stejný příklad přepsal do "klasického" stylu:

Kód: Vybrat vše

unsigned long start_znacka_1;
unsigned long start_znacka_2;
bool blokuj_2 = false;

void blinkLed() {
  digitalWrite(0, !digitalRead(0));
}

void sayHello() {
  digitalWrite(1, HIGH); // rozsvítí LED jen jednou
}

void setup() {
  pinMode(0, OUTPUT);
  pinMode(1, OUTPUT);

  start_znacka_1 = millis();
  start_znacka_2 = millis();
}

void loop()
  {
    if (millis() - start_znacka_2 > 2000 and blokuj_2 == false)
      {
        blokuj_2 = true;
        sayHello();
      }

    if (millis() - start_znacka_1 > 500)
      {
        start_znacka_1 = millis();
        blinkLed();
      }  
  }


Po překladu pro ATtiny85:
Projekt zabírá 636 bytů (7%) úložného místa pro program. Maximum je 8192 bytů.
Globální proměnné zabírají 18 bytů (3%) dynamické paměti, 494 bytů zůstává pro lokální proměnné. Maximum je 512 bytů.
Nejsem žádný fanatický nepřítel objektů, ale na malých procesorech se jim pokud možno vyhýbám.

Uživatelský avatar
SkullKeep
Příspěvky: 316
Registrován: 23 srp 2017, 18:51
Bydliště: Brno
Kontaktovat uživatele:

Re: MINISCHEDULER pro Attiny84/85

Příspěvek od SkullKeep » 14 zář 2025, 18:14

Chápu jednodužší a lepší varianta, ale zas tak moc šetřit nebudu potřebovat, protože to bude jen na jedoduche úkoly a nechci zbytečně používat Delay. Vím že to není ideální ,ale pro mne to stačí.
:twisted: :?: :arrow: :geek: P. Q. M.

Cmrnda
Příspěvky: 47
Registrován: 25 dub 2024, 17:58

Re: MINISCHEDULER pro Attiny84/85

Příspěvek od Cmrnda » 14 zář 2025, 21:48

SkullKeep píše:
14 zář 2025, 18:14
Chápu jednodužší a lepší varianta, ale zas tak moc šetřit nebudu potřebovat, protože to bude jen na jedoduche úkoly a nechci zbytečně používat Delay. Vím že to není ideální ,ale pro mne to stačí.
Delay() se tady taky vyhneš, akorát to neřeší zatížení, takže proces je pomalejší než millis(). Udělal jsem si závěs na millis() a dorovnal veškerý skluz. Má to výhodu v tom že je fuk že se millis() po 1/4 roce, nebo tak nějak přetočí. Prostě to jede furt jako datum.
Jen výtržek z mé synchro.

Kód: Vybrat vše

    if (Nove_casove_synchro == 1)
    {
      if (Loop_Cas - Minuly_Cas_9 == 1000)
      {
        Minuly_Cas_9 = Loop_Cas;

                    // Den = 86 400 sekund
                    Den_sec ++;

                    // Volani
                    Vytvor_mezeru();
                    Vysilani_UDP("Synchronizace casu - pocet vterin - probehlo bez zdrzeni, pripocetla se vterina.");

        //Minuly_Cas_9 = Loop_Cas;
      }

      if (Loop_Cas - Minuly_Cas_9 > 1000)
      {
                  Opozdeny_cas = 0;
                  Opozdeny_cas = Loop_Cas - Minuly_Cas_9 - 1000;

        Minuly_Cas_9 = Loop_Cas;

                  // Den = 86 400 sekund
                  Den_sec = Den_sec + 1;
                  Celkem_opozdeny_cas = Celkem_opozdeny_cas + Opozdeny_cas;
                  Vytvor_mezeru();
                  Vysilani_UDP("Synchronizace casu - pocet vterin - probehla se zdrzenim (ms): " + String(Opozdeny_cas));
                  Vysilani_UDP("Synchronizace casu - pocet vterin - pripocetla se vterina, ostatni milisec byly prevedeny do celkoveho souctu.");
                  Vysilani_UDP("Synchronizace casu - pocet vterin - celkovy soucet zdrzeni (ms): " + String(Celkem_opozdeny_cas));
                    
                      if (Celkem_opozdeny_cas >= 1000)
                      {
                        // Volani
                        Nacti_datum_do_systemu(1); // Odešle na UDP - 1 = celkový datum a čas, 2 = hod+min+sec, 3 = nový (den mesic rok), 4 = 1+2+3

                        Den_sec = Den_sec + 1;
                        Celkem_opozdeny_cas = Celkem_opozdeny_cas - 1000;

                        // Volani
                        Vytvor_mezeru();
                        Vysilani_UDP("Synchronizace casu - pocet vterin - pripocetla se vterina z celkoveho souctu.");
                        
                        if (Celkem_opozdeny_cas == 0)
                        {
                          Vysilani_UDP("Synchronizace casu - pocet vterin - dorovnano (ms): " + String(Celkem_opozdeny_cas));
                        }
                        if (Celkem_opozdeny_cas > 0)
                        {
                          Vysilani_UDP("Synchronizace casu - pocet vterin - zbyle milisec byly prevedeny do celkoveho souctu (ms): " + String(Celkem_opozdeny_cas));
                        }

                        if (Celkem_opozdeny_cas >= 1000)
                        {
                          // Volani
                          Nacti_datum_do_systemu(1); // Odešle na UDP - 1 = celkový datum a čas, 2 = hod+min+sec, 3 = nový (den mesic rok), 4 = 1+2+3

                          Den_sec = Den_sec + 1;
                          Celkem_opozdeny_cas = Celkem_opozdeny_cas - 1000;
                          Vysilani_UDP("Synchronizace casu - pocet vterin - pripocetla se dalsi vterina z celkoveho souctu, kriticka situace.");
                        }
                      }

        //Minuly_Cas_9 = Loop_Cas;
      }
    }

Odpovědět

Kdo je online

Uživatelé prohlížející si toto fórum: Žádní registrovaní uživatelé a 1 host