Code Monkey home page Code Monkey logo

esp8266scheduler's Introduction

Logo

Build Status arduino-library-badge

Quick Start

Installing

You can install through the Arduino Library Manager. The package name is ESP8266Scheduler.

Usage

Include the library in your sketch

#include <Scheduler.h>
#include <Task.h>
#include <LeanTask.h>

In your setup function start the scheduler

Scheduler.start(&task);

Scheduler.begin();

The scheduler blocks once begun, so the loop function is called by the scheduler BEFORE the first task is runned each time. But it is recommended to avoid using a loop.

Creating a Task

Tasks are classes that should inherit the Task class. A task can define a loop() and setup() function much as the normal Arduino standard.

class BlinkTask : public Task {
 protected:
  void setup() {
    state = HIGH;

    pinMode(2, OUTPUT);
    digitalWrite(2, state);
  }

  void loop() {
    state = state == HIGH ? LOW : HIGH;
    digitalWrite(2, state);

    delay(1000);
  }

private:
  uint8_t state;
} blink_task;

IMPORTANT: Tasks must be declared globally on the stack (not a pointer). Failure to do so will crash your device.

Tasks can run yield() and delay() like they normally would. These functions yield control to the scheduler rather than the ESP8266.

Creating a LeanTask

Lean tasks are classes that should inherit the LeanTask class. A lean task can define a loop() and setup() function much as the normal Arduino standard.

LeanTask doesn't use cont.h (runs in the global context), so yield() works exactly as it would without using the library. This can be useful for saving ram (4k) if the task does not use yield() to interrupt the task. You can use delay() at the end of a task to set the interval.

Good example:

class MemTask : public LeanTask {
 public:
  void loop() {
    Serial.print("Free Heap: ");
    Serial.print(ESP.getFreeHeap());
    Serial.println(" bytes");

    delay(10000);
  }
} mem_task;

Bad example. The first delay() does nothing:

Source. DO NOT DO THIS!

class PrintTask : public LeanTask {
 protected:
  void loop() {
    Serial.println("Print Loop Start");

    delay(5000);

    Serial.println("Print Loop End");

    delay(5000);
  }
} print_task;

Tasks can run yield() and delay() like they normally would. The yield() function transfers control to the ESP8266, NOT the scheduler. The delay() function will tell the scheduler that a delay is needed before the next run. If you REALLY need a delay, use ::delay(), but this will block the task and the scheduler.

Tests using Task vs LeanTask

All examples have the same logic. To optimize RAM, use LeanTask (if possible), because each instance of Task requires 4 kb of RAM.

File Description Free heap (more is better)
simple.ino 3 Task 39896 bytes
lean_simple.ino 1 Task, 2 LeanTask 48168 bytes
subfile_simple.ino 1 Task, 2 LeanTask + main loop 48136 bytes

heap_test.ino:

TASK_TYPE Description Free heap (more is better)
Task 12 Task + main loop 2280 bytes
LeanTask 12 LeanTask + main loop 51912 bytes

Task methods

bool AbstractTask::isEnabled();

Method return the status of the task: enabled (true) or disabled (false).


AbstractTask(bool _enabled = true, unsigned long _interval = 0);

Constructor.


bool AbstractTask::isEnabled();

Method return the status of the task: enabled (true) or disabled (false).


void AbstractTask::enable();

The method enable the task.


void AbstractTask::disable();

The method disable the task.


void AbstractTask::setInterval(unsigned long val);

The method sets the task run interval.


unsigned long AbstractTask::getInterval();

The method returns the task run interval.

Scheduler methods

static void SchedulerClass::start(AbstractTask *task);

Adds a task to the multitasking queue.


static void SchedulerClass::begin();

Starts the scheduler. This function is "blocking". It should be the last call the setup function.


static void SchedulerClass::delay(unsigned long ms);

Calls AbstractTask::delay() on the runned task from outside

IMPORTANT: You should not call this method outside for LeanTask tasks. It does not make sense.


static void SchedulerClass::yield();

Calls AbstractTask::yield() on the runned task from outside

IMPORTANT: You should not call this method outside for LeanTask tasks. It does not make sense.

esp8266scheduler's People

Contributors

chris685 avatar dbuezas avatar fricorico avatar laxilef avatar mikefair avatar nrwiersma avatar per1234 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

esp8266scheduler's Issues

cont_yield with ESP8266 Release 3.1.0

Software is no longer buildable with new Release of ESP8266 (https://github.com/esp8266/Arduino).

In file included from Scheduler.cpp:2:
Task.h: In member function 'virtual void Task::yield()':
Task.h:20:5: error: 'cont_yield' was not declared in this scope; did you mean 'can_yield'?
   20 |     cont_yield(&context);
      |     ^~~~~~~~~~
      |     can_yield

Thoughts on adding "signals"

I've gone ahead in my local branch and added "Signals" to Delay.h.
There are 31 signals (using a 32bit bitField) available to use.

It works by a Task issuing a "result = WaitForAny(signals, msTimeout);" or "result = WaitForAll(signals, msTimeout);".

When the Task resumes, if result == 0 then the timeout expired.
Otherwise, result contains all the signals that matched the request.

In the case of WaitForAll, a non-zero result means all the requested signals matched; with WaitForAny, result contains all of the requested signals at the time any one of the active signals matched.

Signals are activated with "setSignals(uint32_t signals);" and cleared with "clearSignals(uint32_t signals)".

Here's some sample code with three Tasks; 1 Worker, 1 Monitor, and 1 Reporter

#define SIG_WORK1DONE 0x01
#define SIG_WORK2DONE 0x02
#define SIG_GREENLIGHT  0x08
#define SIG_REDLIGHT       0x04

class Worker1 : public Task {
  bool loop() {
    delay(150); // Do Work1 Stuff
    setSignal(SIG_WORK1DONE);

    delay(150); // Then do Work2 Stuff
    setSignal(SIG_WORK2DONE);
    return true;
  }
}

class Monitor1 : public Task {
  bool loop() {
    uint32_t result = WaitForAny(SIG_WORK1DONE | SIG_WORK2DONE, 5000);

    if (result == 0) {
        Serial.println("No new results obtained in the last 5 seconds; Initiating "5 second rule" response!");
        // Execute 5 second response;
        clearSignal(SIG_GREENLIGHT);
        setSignal(SIG_REDLIGHT);

    } else {
        Serial.print("New results discovered! ");
        if (result & SIG_WORK1DONE) { Serial.print(" Work1 "); }
        if (result & SIG_WORK2DONE) { Serial.print(" Work2 "); }
        Serial.println(" was completed!");
        clearSignal(SIG_REDLIGHT);
        setSignal(SIG_GREENLIGHT);
    }
    return true;
  }
}
class Reporter1 : public Task {
  bool loop() {
    if (WaitForAll(SIG_WORK1DONE | SIG_WORK2DONE, 20000) != 0) {
        // Non-Zero signal means all signals matched
        clearSignal(SIG_WORK1DONE | SIG_WORK2DONE);
        Serial.println("Both Works have been completed!");

    } else {
        // Zero signal means timeout waiting for match
        Serial.println("The works are still in process!  ");
        Serial.print("Work1 " + (result & SIG_WORK1DONE) ? String("has been completed.  ") : String("is still in process.  "); }
        Serial.print("Work2 " + (result & SIG_WORK2DONE) ? String("has been completed.  ") : String("is still in process.  "); }

    }
    return true;
  }
}

}

It's still a bit of an evolving feature; and I'm looking for some ideas/thoughts on how/when to reset/clear the activated signals. It's clearly related to interrupts and callbacks so their might be some tie in to those systems.

For now, I'm seeing that I/we can leave clearing the signals manually cleared, clear on first match, clear once all Tasks currently requesting the signal have seen it.

I will eventually track down the Windows/Linux/Arduino APIs to see what/how they do it. I think the way it's going to go is you need each type of reset for different things and so it's best to have options to control how/when/if a signal is automatically cleared.


The way it works internally is a "signal" is represented by a bit in a 32bit Field, like an interrupt flag or a GPIO digital pin state.

Each "signal" is a binary power of 2 from 1 to 2^31 (most easily created with bitshifting 1 << signal times) and I expect to primarily be created using #define. As a consequence "signals" can be mixed or reversed using "OR" and "AND".

One of the 32-bits in the field is reserved by the system to indicate whether the test is for "Any" or "All".

Internally it's using the same delay clock that "delay" uses and so works off the same mechanism. I added a section before the timespan test to determine if the appropriate signals have been matched and clear out the delay if they have.

All that said, I expect to wait until after you've had a chance to comment on the existing changes to move things into the Scheduler before posting too much on this one.

Cheers!
Mike

Calling yield and delay outside of tasks

Hello.
Please add yield and delay methods to SchedulerClass
This will allow you to make changes to other classes that are used inside tasks.
I tested this on one of my projects and it doesn't affect how groups work.

Little changes are needed:

void SchedulerClass::begin() {
  current = current->next = first;
  while (1) {
    updateCurrentTask();

    if (current->shouldRun())
      cont_run(&current->context, task_tramponline);

    updateRunGroups();

    ::yield();

    current = current->next;
  }
}

void SchedulerClass::delay(unsigned long ms) { current->delay(ms); }
void SchedulerClass::yield() { current->yield(); }

Example using delay or yield in class instantiated in task

It would be great if you can add an example on how to use a non blocking delay in a other class than the Task itself.

I am now doing it like the example below (based on the Simple.ino example), but it requires changing the scheduler to make delay & yield public

class PrintManager  {
  protected:
    Task* _task;
  public:
    PrintManager(Task* task) {
      _task = task;
    }
    
    void print() {
        Serial.println("Print Loop Start");
        _task->delay(4000);
        Serial.println("Print Loop End");
        _task->delay(4000);
    }
};


class PrintTask : public Task {
public:
    PrintTask() : Task(), printManager((Task *) this){}; 

protected:
    PrintManager printManager;
    void loop()  {      
        printManager.print();
    }
} print_task;

Thanks!

Scheduler v0.1.0 incompatible with ESP8266-core-3.0.0

Running the example code on a ESP-12e(NodeMCU) with the latest firmware produces the fails with following error:

Exception 29: StoreProhibited: A store referenced a page mapped with an attribute that does not permit stores
PC: 0x402012af: SchedulerClass::start(Task*) at .../libraries/ESP8266Scheduler/src/Scheduler.cpp line 23
EXCVADDR: 0x00000004

Decoding stack results
0x402011c5: setup() at ~/Documents/Arduino/compatibility_test/compatibility_test.ino line 55
0x40201cc4: loop_wrapper() at ~/Library/Arduino15/packages/esp8266/hardware/esp8266/3.0.0/cores/esp8266/core_esp8266_main.cpp line 19

This worked fine last month, so I'm assuming the issue from upgrading the core to 3.0.0. Any ideas?

Allow for an extra "free" task

Given that Tasks are memory hungry, would you consider calling the global loop function after all tasks went through a cycle?
Of course that a delay in the global loop would block all tasks, but this "thread" can still be useful for things like ArduinoOTA.handle(); or checking the serial input.

Removal of main Task to save ram

If I read the code correctly, the main Task is just a placeholder to hold prev and next, which is very elegant but it unnecessarily takes 4k of RAM.

declaring it as a pointer and using the first started task in its place saves almost 5k.

Task *SchedulerClass::main = NULL;
Task *SchedulerClass::current = NULL;
SchedulerClass::SchedulerClass() {}

void SchedulerClass::start(Task *task) {
  if (!main) {
    main = task;
    current = main;
    main->next = main;
    main->prev = main;
  }
  task->next = main;
  task->prev = main->prev;

  main->prev->next = task;
  main->prev = task;
}

My sketch seems to be working fine with this and allows me an extra concurrent task.
From experiments, it seems like 24k of free heap are necessary to establish an SSL connection, so this makes a big difference (3 concurrent tasks can run ok)

non-blocking delay in non-member function

Hi there,

thanks for the library. I wanted to ask if there is any chance to use delays of called non-member functions as non-blocking delays for the scheduler. I just can't get it to work.

I could provide a full-code-example but for the sake of simplicity, this would be a very basic example of what i am trying to do.

class SomeTask : public Task {
protected:
    void loop() {
        nonmemberfunction();
    }
} some_task;

void nonmemberfunction(){
   do_something();
   delay(500); // i want to use this delay for other processes
}

Build Issue with PlatformIO

Hi there!
Thanks for providing this code, I can't wait to see it working!

I'm having an issue when building my project including this library using the PlatformIO Visual Studio Code extension.
I have not edited any code in the header files so they appear as in the master branch but there are 7 build issues in Task.h when building my project.

Here is my platformio.ini file

[platformio]
default_envs = nodemcuv2

[common]
build_flags = 
	-D VERSION=1.2.3
	-D DEBUG=1

[env:nodemcuv2]
platform = espressif8266
board = nodemcuv2
framework = arduino
build_flags = 
	${common.build_flags}
	-I$PROJECT_DIR/include
	-fexceptions
lib_deps = 
	fastled/FastLED@^3.6.0
	knolleary/PubSubClient@^2.8
	arduino-libraries/Arduino_JSON@^0.2.0
	nrwiersma/ESP8266Scheduler @ ^1.0
monitor_speed = 115200
test_ignore = test_desktop

And here are the build issues reported in Task.h
image

I've tried deleting and re-adding the library and also restarting my machine but no joy. Is there anything you could suggest?
Please let me know if you need any further information from me :)

Thanks!
Cam

Adding "run_groups" and "data_spaces"

Hi!

I've been working with the ESP8266Scheduler a bit and while I haven't used the official Arduino, or any other "Scheduler", libraries for comparison; I really like it!

One thing about the "one-big-loop" model is you have lots of control over the ordering of if/when things happen and they can share in-common data easily. Scheduler largely eliminates that in favor of coroutines wrapped in their own classes that all run mostly segregated and seemingly in parallel.

I've written some basic/initial code to add a "run_groups" concept to the Scheduler which adds back some degree of those coordination capabilities while keeping everything nice and separated. :)

In a Task code, either setup() or the constructor(), you'd add something like setRunGroupID(N); where currently N is a number 0..7 or 255 (the default).
(The number of groups available is defined by the size of a bitfield used internally; I used an uint8_t.)

By assigning a Task a run_group_id, that Task will be delayed until all Tasks with lower run_group_ids have "succeeded" for this cycle. "Success" means the Task considers its work completed and will wait for the next "cycle" before resuming. So it could mean the Task "timed out" on what it was doing and didn't want to hold up downstream RunGroups from processing any longer.

RunGroup 0xFF is the default RunGroup, and it's also the exception to the rule. Tasks with run_group_id 0xFF use the current behavior - other run_groups do not wait for them, and they do not wait for anything else (other than their own shouldRun()).

Excluding that exception, groups of Tasks will execute in the order of their run_group_id in a cycle. Higher numbered RunGroups will delay until all Tasks in the lower RunGroups have marked themselves complete.

When all the Tasks in all RunGroups have executed, the cycle_id number increments and the Tasks' completed state get reset.

Here's an example from 3 Tasks to explain it more clearly.
2 Tasks "Acquire" data then 1 "Processes" it.

Notice the cycle_id (the first number) remains the same until all three Tasks have run and that the two "Acquiring" Tasks Finish before the "Processing" Task begins; and the cycle_id increments only after the "Processing" Task completes:

[Cycle_ID] ([Task_Id]) [Function]... [Internal Counter]
2	(1.1) Acquiring... 1
2	(1.2) Acquiring... 1
2	(1.2) Acquiring... 2
2	(1.1) Acquiring... 2
2	(1.2) Acquiring... 3
2	(1.2) Acquiring... 4
2	(1.1) Acquiring... 3
2	(1.2) Acquiring... 5
2	(1.2) Acquiring... 5 Done!
2	(1.1) Acquiring... 4
2	(1.1) Acquiring... 5
2	(1.1) Acquiring... 5 Done!
2		(5.1) Processing... 1
2		(5.1) Processing... 2
2		(5.1) Processing... 3
2		(5.1) Processing... 4
2		(5.1) Processing... 5
2		(5.1) Processing... 5 Done!
3	(1.1) Acquiring... 1
3	(1.2) Acquiring... 1
3	(1.2) Acquiring... 2
3	(1.2) Acquiring... 3
3	(1.1) Acquiring... 2
3	(1.2) Acquiring... 4
3	(1.1) Acquiring... 3
3	(1.2) Acquiring... 5
3	(1.2) Acquiring... 5 Done!
3	(1.1) Acquiring... 4
3	(1.1) Acquiring... 5
3	(1.1) Acquiring... 5 Done!
3		(5.1) Processing... 1
3		(5.1) Processing... 2
3		(5.1) Processing... 3
3		(5.1) Processing... 4
3		(5.1) Processing... 5
3		(5.1) Processing... 5 Done!
4	(1.1) Acquiring... 1
4	(1.2) Acquiring... 1
4	(1.2) Acquiring... 2
4	(1.2) Acquiring... 3
4	(1.1) Acquiring... 2
4	(1.2) Acquiring... 4
4	(1.2) Acquiring... 5
4	(1.2) Acquiring... 5 Done!
4	(1.1) Acquiring... 3

In this example, no Task had run_group_id 0xFF because its output more cluttered and confused the example than helped it.

If there's interest, I can explain the internals of how it works a bit more; and discuss modifications for merging it. While it's not "trivial", it's not a big diff.

One of the things that I put a lot of time into is keeping the amount of state required to as little as I could imagine (but no less :) ) and making the update functions as efficient/quick as I could (for instance, the new code never makes a run through the entire list of Tasks; it does it all "inline" as the Scheduler reaches them).

The SchedulerClass added 4 bytes; and the Tasks 2 bytes and 2 bools.

One thing I wanted that I couldn't do easily was get "loop()" to return a bool rather than a void.
I think that would make things cleaner/clearer to understand.

Thanks!

Memory usage.

Is possible that using:
Class Bla : Task {
} bla;
eats more ram (like 4k) than:
class Bla : Task {};
static Bla bla;

Documentation has a "typo"

Hey, it's not an issue;
Is something I've found, that maybe you made a mistake, because to write a digital output, we should use digitalWrite:

Current doc:
`class BlinkTask : public Task {
protected:
void setup() {
state = HIGH;

    pinMode(2, OUTPUT);
    pinMode(2, state);
}

void loop() {
    state = state == HIGH ? LOW : HIGH;
    **pinMode(2, state);**

    delay(1000);
}

private:
uint8_t state;
} blink_task;`,

This bolded line should be replaced with
digitalWrite(2, state)

Using ESP8266Scheduler to make HTTPS requests without blocking loop().

Hello,

I am considering using ESP8266Scheduler to make HTTPS requests every few seconds without blocking execution of the main loop() function. Is ESP8266Scheduler designed for this kind of use case? I am having a hard time determining if the scheduled tasks run without blocking the main loop().

Problems with: while(WiFiMulti.run() != WL_CONNECTED){}

Hi!
You have done a great job, this scheduler is really fine!
I'm a beginer.

I'm doing a code, but when arrive a part like this:

while(WiFiMulti.run()` != WL_CONNECTED) 
    {
    }

The part is ignored.

I did two tests:

  • First, I put the while section on general void setup (before indicating the task to do and before begining scheduler).

  • Second, puting this on a task to do

Two tests, same results.
I will appreciate all your help!

Arduino compatibility

Can this run on AVR? I always use your library for my ESP projects, but I am trying to make a small project with a screen that runs on both platforms. Any ideas?

Any online request causing delay on the main loop

I have one task with simple request to REST api and if i'll start in main esp loop is waiting for response...

class ApiRequest : public Task {
  public:
    void loop() {
      Serial.println("ApiRequest");
      if (WiFi.status() == WL_CONNECTED) {
        client.get("/basementData");
        int statusCode = client.responseStatusCode();
        String resBody = client.responseBody();
        //        Serial.println("-------------------------");
        //        Serial.println(resBody);
        if (statusCode = 200) {
          JsonObject root = doc.as<JsonObject>();
          auto error = deserializeJson(doc, resBody);
          if (!error) {
            //            double stoveTemp = root["stoveTemperature"];
            stoveTemperature = (int)root["stoveTemperature"];
            waterTemperature = (int)root["waterTemperature"];
            timeMills = root["timeStamp"];
          }
        }
      }
    }
} api_task;

cont_suspend was not declared in this scope

Hey,
I am new to the scheduling stuff, but I tried out your simple example and I'm getting this error(screenshot attached)
image

The code is the same as simple example posted on main github page of this repo.
Possible Cause: As far as I researched, it seems to be the cont.h header file problem, since I checked out the cont.h file from Arduino and turns out, there is cont_suspend method in it. But I guess, my IDE is not finding the reference to it.

Please help me find the issue

Thanks!

task not working after several hours

As title, I'm trying to use Scheduler to manage different task, it works very well at the beginning, but after several hours, some task die(but others are still alive). below is my test code. the encoder task will die after about 2 hours after each reboot. Please kindly help me to figure out the problem.

`
// 需要用到的库
#include <Arduino.h>
#include <Scheduler.h> // 分时管理
#include <ClickEncoder.h> // 旋钮按键
#include <Adafruit_Sensor.h>
#include <DHT.h> // DHT22 传感器库
#include <brzo_i2c.h> // I2C
#include <SH1106Brzo.h> // OLED

#define ENCODER_PINA D4 // 对应GPIO2,印刷版上D4
#define ENCODER_PINB D7 // 对应GPIO13,印刷版上D7
#define ENCODER_BTN D6 // 对应GPIO12,印刷版上D6
#define ENCODER_STEPS_PER_NOTCH 4

#define DHT_PIN D5 // 对应GPIO14,印刷版上D5
#define DHT_TYPE 11 // DHT22温湿度传感器

#define OLED_ADDRESS 0x3C
#define OLED_SCL D1
#define OLED_SDA D2

#define GLYPH_WIDTH 32
#define GLYPH_HEIGHT 64
// 图形
static const char glyphs[4][GLYPH_WIDTH * GLYPH_HEIGHT] PROGMEM = {
{ // 图案0,关闭状态
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xFC, 0x3F, 0x00,
0x00, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x01, 0xC0, 0xFF, 0xFF, 0x03,
0xE0, 0x7F, 0xFE, 0x07, 0xF0, 0x7F, 0xFE, 0x0F, 0xF8, 0x7F, 0xFE, 0x0F,
0xF8, 0x79, 0x9E, 0x1F, 0xFC, 0x78, 0x1E, 0x1F, 0x7C, 0x7E, 0x7E, 0x3E,
0x7C, 0x7E, 0x7E, 0x3E, 0x7E, 0x7F, 0xFE, 0x3E, 0x3E, 0x7F, 0xFE, 0x7C,
0x3E, 0x7F, 0xFE, 0x7C, 0x3E, 0x7F, 0xFE, 0x7C, 0x3E, 0x7F, 0xFE, 0x7C,
0x3C, 0x7F, 0xFE, 0x3C, 0x7C, 0xFE, 0x7F, 0x3E, 0x7C, 0xFE, 0x7F, 0x3E,
0xFC, 0xFC, 0x3F, 0x1F, 0xF8, 0xF9, 0x8F, 0x1F, 0xF0, 0xE3, 0xC7, 0x0F,
0xF0, 0x07, 0xE0, 0x0F, 0xE0, 0x1F, 0xF8, 0x07, 0xC0, 0xFF, 0xFF, 0x03,
0x80, 0xFF, 0xFF, 0x01, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xF8, 0x1F, 0x00,
0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
},
{ // 图案1,加热状态
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00,
0x00, 0xE0, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00,
0x00, 0xF8, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00,
0x00, 0xFC, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00,
0x00, 0xFE, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x08, 0x00, 0xFE, 0x01, 0x0C,
0x00, 0xFE, 0x01, 0x0E, 0x00, 0xFE, 0x01, 0x07, 0x00, 0xFC, 0x03, 0x07,
0x20, 0xFC, 0x87, 0x07, 0x60, 0xFC, 0x87, 0x07, 0xE0, 0xFC, 0xCF, 0x0F,
0xE0, 0xF9, 0xDF, 0x0F, 0xE0, 0xF9, 0xFF, 0x0F, 0xE0, 0xF9, 0xFF, 0x0F,
0xE0, 0xF9, 0xFF, 0x0F, 0xE0, 0xF9, 0xFF, 0x1F, 0xF0, 0xF9, 0xFF, 0x1F,
0xF0, 0xFD, 0xFF, 0x1F, 0xF8, 0xFD, 0xFD, 0x3F, 0xFC, 0xFF, 0xF9, 0x3F,
0xFC, 0xFF, 0xF9, 0x7F, 0xFE, 0xFF, 0xF9, 0x7F, 0xFE, 0xFF, 0xF8, 0x7F,
0xFE, 0xF7, 0xF8, 0x7F, 0xFF, 0xF7, 0xF8, 0x7F, 0xFF, 0x67, 0xD8, 0x7F,
0xFF, 0x67, 0xC8, 0x7F, 0xFF, 0x27, 0xC8, 0x7F, 0xFF, 0x03, 0xC8, 0x7F,
0xFF, 0x03, 0xC8, 0x7F, 0xFE, 0x03, 0x88, 0x7F, 0xFE, 0x01, 0x80, 0x3F,
0xFC, 0x01, 0x00, 0x3F, 0xFC, 0x01, 0x00, 0x1F, 0xF8, 0x01, 0x00, 0x0F,
0xF0, 0x01, 0x00, 0x07, 0xE0, 0x03, 0x80, 0x03, 0x80, 0x07, 0xC0, 0x00,
0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
},
{ // 图案2,制冷状态
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00,
0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xE0, 0x07, 0x00,
0x00, 0xF0, 0x0F, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xF8, 0x1F, 0x00,
0x00, 0xFC, 0x3F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x7F, 0x00,
0x00, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x01, 0x80, 0xFF, 0xFF, 0x01,
0xC0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x0F,
0xF0, 0x7F, 0xFE, 0x0F, 0xF8, 0x5F, 0xF8, 0x1F, 0xFC, 0x1F, 0xFC, 0x3F,
0xFC, 0x3D, 0xBC, 0x3F, 0xFC, 0x7D, 0x9E, 0x3F, 0x3E, 0x78, 0x1E, 0x7C,
0xFE, 0x78, 0x1E, 0x7F, 0x3E, 0x60, 0x06, 0x7C, 0x3E, 0x46, 0x62, 0x7C,
0xFE, 0x1F, 0xF8, 0x7F, 0xFE, 0x3F, 0xFC, 0x7F, 0xFE, 0x0F, 0xF0, 0x7F,
0x3E, 0x40, 0x22, 0x7C, 0x7E, 0x70, 0x0E, 0x7F, 0x7E, 0x78, 0x1E, 0x7E,
0x3C, 0x7C, 0x1E, 0x3C, 0xFC, 0x7D, 0x9E, 0x3F, 0xF8, 0x3D, 0xBC, 0x1F,
0xF8, 0x1F, 0xF8, 0x1F, 0xF0, 0x5F, 0xFA, 0x0F, 0xE0, 0x7F, 0xFE, 0x07,
0xC0, 0xFF, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFE, 0x7F, 0x00,
0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
},
{ // 图案3,自动状态
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xFC, 0x3F, 0x00,
0x00, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x01, 0xC0, 0xFF, 0xFF, 0x03,
0xE0, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0x0F,
0xF8, 0xFF, 0xFF, 0x1F, 0xFC, 0xFF, 0xFF, 0x1F, 0xBC, 0xBB, 0x60, 0x3C,
0xBC, 0xBB, 0xBB, 0x3B, 0x5E, 0xBB, 0xBB, 0x3B, 0x5E, 0xBB, 0xBB, 0x7B,
0x5E, 0xBB, 0xBB, 0x7B, 0x0E, 0xBA, 0xBB, 0x7B, 0xEE, 0xBA, 0xBB, 0x7B,
0xEC, 0xC6, 0x7B, 0x3C, 0xFC, 0xFF, 0xFF, 0x3F, 0xFC, 0xFF, 0xFF, 0x3F,
0xFC, 0xFF, 0xFF, 0x1F, 0xF8, 0xFF, 0xFF, 0x1F, 0xF0, 0xFF, 0xFF, 0x0F,
0xF0, 0xFF, 0xFF, 0x0F, 0xE0, 0xFF, 0xFF, 0x07, 0xC0, 0xFF, 0xFF, 0x03,
0x80, 0xFF, 0xFF, 0x01, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xF8, 0x1F, 0x00,
0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
}
};

ClickEncoder encoder = ClickEncoder(ENCODER_PINA, ENCODER_PINB, ENCODER_BTN, ENCODER_STEPS_PER_NOTCH);
DHT dht(DHT_PIN,DHT_TYPE);
SH1106Brzo oled(OLED_ADDRESS, OLED_SDA, OLED_SCL);

volatile uint8_t targetTemperature = 20;
volatile uint8_t currentHumidity = 20;
volatile uint8_t currentTemperature = 20;
volatile uint8_t targetMode = 0, currentMode = 0;
volatile bool isOledOn = true;

// 编码器管理进程
class EncoderTask : public Task {
protected:
void setup() {
encoder.setDoubleClickEnabled(true);
encoder.setButtonHeldEnabled(false);
encoder.setButtonOnPinZeroEnabled(false);
}
void loop() {

    static uint32_t lastService = 0;
    if (lastService + 1000 < micros()) {
            lastService = micros();
            encoder.service();
    }

    static int16_t encLast, encValue = 0;
    encValue += encoder.getValue();
    if(encValue != encLast) {
            if(isOledOn) { // 只有在屏幕点亮的时候才调整温度,避免误操作
                    targetTemperature = (encValue > encLast) ? targetTemperature + 1 : targetTemperature - 1;
                    if(targetTemperature > 38) targetTemperature = 38;
                    if(targetTemperature <10) targetTemperature = 10;
            }
            encLast = encValue;
    }

    ClickEncoder::Button b = encoder.getButton();
    if (b != ClickEncoder::Open) {
            switch (b) {
            case ClickEncoder::Clicked:
                    isOledOn = !isOledOn;
                    break;
            case ClickEncoder::DoubleClicked:
                    if(isOledOn) { // 只有在屏幕点亮的时候操作切换模式才有效,避免误操作
                            currentMode += 1;
                            if(currentMode > 3) currentMode = 0;
                    }
                    break;
            }
    }

}
bool shouldRun() {
bool run = Task::shouldRun();

    // Your code here
    //Serial.println("This task should be running");
    return run;

}
} encoder_task;

// DHT温湿度传感器管理进程
class DHTTask : public Task {
public:
void setup() {
pinMode(DHT_PIN, INPUT);
}
void loop() {
currentTemperature = (int)dht.readTemperature();
currentHumidity = (int)dht.readHumidity();
static bool state = false;
state = !state;
Serial.print(state);
Serial.print(": Target: ");
Serial.println(targetTemperature);
delay(10000); // 每10秒采集一次温湿度数据
}
} dht_task;

// OLED显示管理进程
class OledTask : public Task {
public:
void setup() {
// 初始化OLED液晶屏并显示初始提示
oled.init();
//oled.flipScreenVertically();
oled.setColor(WHITE);
oled.setFont(ArialMT_Plain_24);
oled.setTextAlignment(TEXT_ALIGN_LEFT);
oled.clear();
oled.drawString(40, 12, "INIT");
oled.display();
}
void loop() {
char ctemp[3], humi[3], ttemp[3];
sprintf(ctemp, "%d", currentTemperature);
sprintf(humi, "%d", currentHumidity); // not used yet.
sprintf(ttemp, "%d", targetTemperature);
oled.clear();
// 画模式图案
oled.drawXbm(16, 0, GLYPH_WIDTH, GLYPH_HEIGHT, glyphs[currentMode]);
// 画个房子
oled.drawLine(10, 14, 26, 0);
oled.drawLine(26, 0, 74, 30);
oled.drawLine(74, 30, 58, 30);
oled.drawLine(58, 30, 58, 64);
oled.drawString(78, 1, ctemp);
oled.drawString(78, 32, ttemp);
oled.display();
if(isOledOn) oled.displayOn();
else oled.displayOff();
}
} oled_task;

// 主设置程序
void setup() {
Serial.begin(115200);

    Serial.println("");

    delay(1000);

    // 加入各个任务
    Scheduler.start(&encoder_task);
    Scheduler.start(&dht_task);
    Scheduler.start(&oled_task);

    // 启动任务的执行
    Scheduler.begin();

}

// 主循环
void loop() {
}
`

esp32

This library is great! Can you port this code to esp32?

Delay in subroutines

Subroutines are not calling the correct delay function in Task but rather the default delay function that returns control to the esp.

Can't work with UniversalTelegramBot lib

Great library that i could run multiple task without blocking each other, its also work perfectly using firebase as my server. But found some issue that UniversalTelegramBot from couldn't work with this library. It does compiled without any problems, but there's no sign that it is connected with the telegram server. Here's my code.

#include <Arduino.h>
#include <Scheduler.h>
#include <ArduinoJson.h>
#include <SoftwareSerial.h>
#include <WiFiManager.h>
#include <WiFiClientSecure.h>
#include <UniversalTelegramBot.h>

SoftwareSerial stm(14, 12);
DynamicJsonDocument senD(768);
DynamicJsonDocument geT(768);

float voltage = 0.00;
float current = 0.00;
bool state = 0;
int b, num, treshold;
String err;

WiFiManager wm;

#define BOT_TOKEN "------------------------------------"
X509List cert(TELEGRAM_CERTIFICATE_ROOT);
WiFiClientSecure secured_client;
UniversalTelegramBot bot(BOT_TOKEN, secured_client);

class Telegram : public Task {
  protected:
    void handleNewMessages(int numNewMessages){
      Serial.print("handleNewMessages ");
      Serial.println(numNewMessages);
    
      for (int i = 0; i < numNewMessages; i++) {
        String chat_id = bot.messages[i].chat_id;
        String text = bot.messages[i].text;
    
        String from_name = bot.messages[i].from_name;
        if (from_name == "")
          from_name = "Guest";
    
        if (text == "/Pasang")
        {
          digitalWrite(2, LOW); // turn the LED on (HIGH is the voltage level)
          bot.sendMessage(chat_id, "Connected!", "");
          state = 1;
        }
    
        if (text == "/Putus")
        {
          //          ledStatus = 0;
          digitalWrite(2, HIGH); // turn the LED off (LOW is the voltage level)
          bot.sendMessage(chat_id, "Disconnected!", "");
          state = 0;
        }
    
        if (text == "/Status")
        {
          if (digitalRead(2))
          {
            if(current > treshold){
              bot.sendMessage(chat_id, "Voltage :" + String(voltage) + "\nCurrent :" + String(current) + "| Power : " + String(treshold) + "\nSomething suspicious!", "");
            } else{
              bot.sendMessage(chat_id, "Voltage :" + String(voltage) + "\nCurrent :" + String(current) + "| Power : " + String(treshold), "");
            }
          }
          else
          {
            bot.sendMessage(chat_id, "Voltage :" + String(voltage) + "\nCurrent :" + String(current) + "| Power : " + String(treshold), "");
          }
        }
      }
    }
    void setup(){
      configTime(0, 0, "pool.ntp.org");      // get UTC time via NTP
      secured_client.setTrustAnchors(&cert); // Add root certificate for api.telegram.org
    }
    void loop(){
      if (millis() - bot_lasttime > BOT_MTBS)
      {
        int numNewMessages = bot.getUpdates(bot.last_message_received + 1);
    
        while (numNewMessages)
        {
          Serial.println("got response");
          handleNewMessages(numNewMessages);
          numNewMessages = bot.getUpdates(bot.last_message_received + 1);
        }
    
        bot_lasttime = millis();
      }      
    }
  private:
    const unsigned long BOT_MTBS = 100; // mean time between scan messages
    unsigned long bot_lasttime; // last time messages' scan has been done
} telegram;
class ConnectWiFi : public Task {
  protected:
    void setup(){
      //reset settings - for testing
      //wm.resetSettings();
      WiFiManagerParameter v("mcb", "Input The Treshold", "", 50);
      wm.addParameter(&v);
      wm.setDebugOutput(false);
      wm.setConfigPortalTimeout(120);
      
      if (WiFi.status() != WL_CONNECTED) {
          if (!wm.autoConnect()) {
            Serial.println("failed to connect and hit timeout");
            delay(3000);
            //reset and try again, or maybe put it to deep sleep
            ESP.restart();
            delay(5000);
          }
          treshold = atoi(v.getValue());
      }
    }
    
    void loop(){
        if (WiFi.status() != WL_CONNECTED) {
            if (!wm.autoConnect()) {
              Serial.println("failed to connect and hit timeout");
              delay(3000);
              //reset and try again, or maybe put it to deep sleep
              ESP.restart();
              delay(5000);
            }
        }
        Serial.print("WiFi connected at ");
        Serial.println(WiFi.localIP());
        delay(5000);
    }
} wifi_conn;

class printSerial : public Task {
protected:
    void loop()  {
        Serial.println("Random : " + String(state));
        if(err!=""){
            Serial.print(F("deserializeJson() failed with code "));
            Serial.println(err);
        }
        Serial.println("Get: " + String(num));
        delay(500);
    }
} printSerial;

class Json : public Task {
protected:
    void loop()  {
          b  = state;
          senD["num2"] = b; 
          serializeJson(senD, stm);
                    
          DeserializationError error = deserializeJson(geT, stm);
          err = "";
          if (error) {
            err = error.c_str();
        //    return;
          }
          num = geT["num"];
          state = !state;
          digitalWrite(2, !state);
          delay(500);
    }
} Json;

void setup() {
    WiFi.mode(WIFI_STA);
    Serial.begin(115200);
    stm.begin(9600);
    pinMode(2, OUTPUT);
    delay(1000);

    Scheduler.start(&telegram);
    Scheduler.start(&wifi_conn);
    Scheduler.start(&printSerial);
    Scheduler.start(&Json);
   
    Scheduler.begin();
}

void loop() {}

Overloading loop()

schedul_test:6:10: error: 'void PrintTask::loop()' cannot be overloaded
void loop() {
^
schedul_test:62:6: error: with 'void PrintTask::loop()'
void loop() {}
^
exit status 1
'void PrintTask::loop()' cannot be overloaded

I try the example file, but got this error.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.