Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add light versions. #32

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions StaticThreadControllerLite.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
StaticThreadControllerLite.h - Controlls a list of ThreadsLite with different timings

Basicaly, what it does is to keep track of current ThreadsLite and run when
necessary.

StaticThreadControllerLite is based on the class StaticThreadController,
but takes less memory and maximum period is only 32,767 seconds.
WARNING! It is not recommended to use if you do not have a memory deficit.

Copyright © 2016 Alex Eremin.
Copyright © 2018 Stanislav Hnatiuk.
Released into the public domain.
*/

#ifndef STATICTHREADCONTROLLERLITE_H
#define STATICTHREADCONTROLLERLITE_H

#include "ThreadLite.h"

template <uint8_t N>
class StaticThreadControllerLite {
protected:
ThreadLite thread[N];
public:
template <typename... T>
StaticThreadControllerLite(T... params) : thread{params...} { };

// Runs all ThreadLite
void run() {
for (uint8_t i = 0; i < N; i++) {
// Is enabled? Timeout exceeded?
if (thread[i].shouldRun()) {
thread[i].run();
}
}
}

// Return the quantity of ThreadsLite
static constexpr int size() {
return N;
};

// Return the I ThreadLite on the array
// Doesn't perform any bounds checks and behaviour is
// unpredictable in case of index > N
ThreadLite& operator[](uint8_t index) {
return thread[index];
}
};

#endif // STATICTHREADCONTROLLERLITE_H
42 changes: 42 additions & 0 deletions ThreadLite.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright © 2013 Ivan Seidel Gomes.
// Copyright © 2018 Stanislav Hnatiuk.

#include "ThreadLite.h"

ThreadLite::ThreadLite(void (*callback)(void), TIME_INT _interval) {
_onRun = callback;
_cached_next_run = 0;
last_run = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoiding to initialize last_run may lead to missed first interval.
For example, if we set very small interval, for example 10ms and current millis() is greater than 32780 (we still use unsigned type, so it can be) then _cached_next_run will be 10 (lasr_run + interval) and sign bit will be set for all the operations until we overflow and thus should_run() will return false.
So for the first run we have to wait around 32 seconds instead of 10ms.

It's kind of rare situation, but I think last_run should be initialized to millis() here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I had a special case in my project and could not use initialization to save memory.

setInterval(_interval);
};

void ThreadLite::runned() {
// Saves last_run
last_run = millis();

// Cache next run
_cached_next_run = last_run + interval;
}

void ThreadLite::setInterval(TIME_INT _interval) {
// Save interval
interval = _interval;

// Cache the next run based on the last_run
_cached_next_run = last_run + interval;
}

bool ThreadLite::shouldRun() {
TIME_INT time = millis();
// If the "sign" bit is set the signed difference would be negative
bool time_remaining = (time - _cached_next_run) & TIME_OVERFLOW;

// Exceeded the time limit, AND is enabled? Then should run...
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Exceeded the time limit? Then should run...

ThreadLite is always enabled :)

return !time_remaining;
}

void ThreadLite::run() {
_onRun();
// Update last_run and _cached_next_run
runned();
}
64 changes: 64 additions & 0 deletions ThreadLite.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
ThreadLite.h - An runnable object

Thread is responsable for holding the "action" for something,
also, it responds if it "should" or "should not" run, based on
the current time;

ThreadLite is based on the class Thread, but takes less memory
and maximum period is only 32,767 seconds.
WARNING! It is not recommended to use if you do not have a memory deficit.

Copyright © 2013 Ivan Seidel Gomes.
Copyright © 2018 Stanislav Hnatiuk.
Released into the public domain.
*/

#ifndef THREADLITE_H
#define THREADLITE_H

#if defined(ARDUINO) && ARDUINO >= 100
#include <Arduino.h>
#endif

#define TIME_INT uint16_t
#define TIME_OVERFLOW 0x8000 // uint16_t = 0x8000 or uint32_t = 0x80000000
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just curious why do you define a new type in that way? Do you need extra flexibility to easily change underlying type? Don't you consider to make a template class in this case?
Anyway, why do you use define instead of typedef or using?
Also overflow value can be defined based on type information, something like this:

using TIME_INT=uint16_t;
constexpr TIME_INT TIME_OVERFLOW = (1 << (sizeof (TIME_INT) * 8 - 1));

Seems it should be done in the mainline too (instead of using 0x80000000)...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for giving this correction. I used bad practice before.


class ThreadLite {
protected:
// Desired interval between runs
TIME_INT interval;

// Last runned time in Ms
TIME_INT last_run;

// Scheduled run in Ms (MUST BE CACHED)
TIME_INT _cached_next_run;

/*
IMPORTANT! Run after all calls to run()
Updates last_run and cache next run.
NOTE: This MUST be called if extending
this class and implementing run() method
*/

// Default is to mark it runned "now"
void runned();

// Callback for run() if not implemented
void (*_onRun)(void);

public:
ThreadLite(void (*callback)(void), TIME_INT _interval);

// Set the desired interval for calls, and update _cached_next_run
void setInterval(TIME_INT _interval);

// Default is to check whether it should run "now"
bool shouldRun();

// Runs ThreadLite
void run();
};

#endif // THREADLITE_H