Skip to content

Commit a2a55f4

Browse files
committed
Merge branch 'add-schedule'
2 parents f0d4886 + 0457587 commit a2a55f4

File tree

8 files changed

+1040
-1
lines changed

8 files changed

+1040
-1
lines changed

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ set(LIBRARY_SRCS
5151
libraries/HTTPUpdate/src/HTTPUpdate.cpp
5252
libraries/NetBIOS/src/NetBIOS.cpp
5353
libraries/Preferences/src/Preferences.cpp
54+
libraries/Schedule/src/Schedule.cpp
5455
libraries/SD_MMC/src/SD_MMC.cpp
5556
libraries/SD/src/SD.cpp
5657
libraries/SD/src/sd_diskio.cpp
@@ -189,7 +190,9 @@ set(COMPONENT_ADD_INCLUDEDIRS
189190
libraries/HTTPClient/src
190191
libraries/HTTPUpdate/src
191192
libraries/NetBIOS/src
193+
libraries/PolledTimeout/src
192194
libraries/Preferences/src
195+
libraries/Schedule/src
193196
libraries/SD_MMC/src
194197
libraries/SD/src
195198
libraries/SimpleBLE/src
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
name=PolledTimeout
2+
version=1.0
3+
author=
4+
maintainer=
5+
sentence=
6+
paragraph=
7+
category=Other
8+
url=
9+
architectures=esp32
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
#ifndef __POLLEDTIMING_H__
2+
#define __POLLEDTIMING_H__
3+
4+
5+
/*
6+
PolledTimeout.h - Encapsulation of a polled Timeout
7+
8+
Copyright (c) 2018 Daniel Salazar. All rights reserved.
9+
This file is part of the esp8266 core for Arduino environment.
10+
11+
This library is free software; you can redistribute it and/or
12+
modify it under the terms of the GNU Lesser General Public
13+
License as published by the Free Software Foundation; either
14+
version 2.1 of the License, or (at your option) any later version.
15+
16+
This library is distributed in the hope that it will be useful,
17+
but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19+
Lesser General Public License for more details.
20+
21+
You should have received a copy of the GNU Lesser General Public
22+
License along with this library; if not, write to the Free Software
23+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24+
*/
25+
26+
#include <limits>
27+
28+
#include <Arduino.h>
29+
30+
namespace esp8266
31+
{
32+
33+
34+
namespace polledTimeout
35+
{
36+
37+
namespace YieldPolicy
38+
{
39+
40+
struct DoNothing
41+
{
42+
static void execute() {}
43+
};
44+
45+
struct YieldOrSkip
46+
{
47+
static void execute() {delay(0);}
48+
};
49+
50+
template <unsigned long delayMs>
51+
struct YieldAndDelayMs
52+
{
53+
static void execute() {delay(delayMs);}
54+
};
55+
56+
} //YieldPolicy
57+
58+
namespace TimePolicy
59+
{
60+
61+
struct TimeSourceMillis
62+
{
63+
// time policy in milli-seconds based on millis()
64+
65+
using timeType = decltype(millis());
66+
static timeType time() {return millis();}
67+
static constexpr timeType ticksPerSecond = 1000;
68+
static constexpr timeType ticksPerSecondMax = 1000;
69+
};
70+
71+
struct TimeSourceCycles
72+
{
73+
// time policy based on ESP.getCycleCount()
74+
// this particular time measurement is intended to be called very often
75+
// (every loop, every yield)
76+
77+
using timeType = decltype(ESP.getCycleCount());
78+
static timeType time() {return ESP.getCycleCount();}
79+
static constexpr timeType ticksPerSecond = F_CPU; // 80'000'000 or 160'000'000 Hz
80+
static constexpr timeType ticksPerSecondMax = 160000000; // 160MHz
81+
};
82+
83+
template <typename TimeSourceType, unsigned long long second_th>
84+
// "second_th" units of timeType for one second
85+
struct TimeUnit
86+
{
87+
using timeType = typename TimeSourceType::timeType;
88+
89+
#if __GNUC__ < 5
90+
// gcc-4.8 cannot compile the constexpr-only version of this function
91+
// using #defines instead luckily works
92+
static constexpr timeType computeRangeCompensation ()
93+
{
94+
#define number_of_secondTh_in_one_tick ((1.0 * second_th) / ticksPerSecond)
95+
#define fractional (number_of_secondTh_in_one_tick - (long)number_of_secondTh_in_one_tick)
96+
97+
return ({
98+
fractional == 0?
99+
1: // no need for compensation
100+
(number_of_secondTh_in_one_tick / fractional) + 0.5; // scalar multiplier allowing exact division
101+
});
102+
103+
#undef number_of_secondTh_in_one_tick
104+
#undef fractional
105+
}
106+
#else
107+
static constexpr timeType computeRangeCompensation ()
108+
{
109+
return ({
110+
constexpr double number_of_secondTh_in_one_tick = (1.0 * second_th) / ticksPerSecond;
111+
constexpr double fractional = number_of_secondTh_in_one_tick - (long)number_of_secondTh_in_one_tick;
112+
fractional == 0?
113+
1: // no need for compensation
114+
(number_of_secondTh_in_one_tick / fractional) + 0.5; // scalar multiplier allowing exact division
115+
});
116+
}
117+
#endif
118+
119+
static constexpr timeType ticksPerSecond = TimeSourceType::ticksPerSecond;
120+
static constexpr timeType ticksPerSecondMax = TimeSourceType::ticksPerSecondMax;
121+
static constexpr timeType rangeCompensate = computeRangeCompensation();
122+
static constexpr timeType user2UnitMultiplierMax = (ticksPerSecondMax * rangeCompensate) / second_th;
123+
static constexpr timeType user2UnitMultiplier = (ticksPerSecond * rangeCompensate) / second_th;
124+
static constexpr timeType user2UnitDivider = rangeCompensate;
125+
// std::numeric_limits<timeType>::max() is reserved
126+
static constexpr timeType timeMax = (std::numeric_limits<timeType>::max() - 1) / user2UnitMultiplierMax;
127+
128+
static timeType toTimeTypeUnit (const timeType userUnit) {return (userUnit * user2UnitMultiplier) / user2UnitDivider;}
129+
static timeType toUserUnit (const timeType internalUnit) {return (internalUnit * user2UnitDivider) / user2UnitMultiplier;}
130+
static timeType time () {return TimeSourceType::time();}
131+
};
132+
133+
using TimeMillis = TimeUnit< TimeSourceMillis, 1000 >;
134+
using TimeFastMillis = TimeUnit< TimeSourceCycles, 1000 >;
135+
using TimeFastMicros = TimeUnit< TimeSourceCycles, 1000000 >;
136+
using TimeFastNanos = TimeUnit< TimeSourceCycles, 1000000000 >;
137+
138+
} //TimePolicy
139+
140+
template <bool PeriodicT, typename YieldPolicyT = YieldPolicy::DoNothing, typename TimePolicyT = TimePolicy::TimeMillis>
141+
class timeoutTemplate
142+
{
143+
public:
144+
using timeType = typename TimePolicyT::timeType;
145+
static_assert(std::is_unsigned<timeType>::value == true, "timeType must be unsigned");
146+
147+
static constexpr timeType alwaysExpired = 0;
148+
static constexpr timeType neverExpires = std::numeric_limits<timeType>::max();
149+
static constexpr timeType rangeCompensate = TimePolicyT::rangeCompensate; //debug
150+
151+
timeoutTemplate(const timeType userTimeout)
152+
{
153+
reset(userTimeout);
154+
}
155+
156+
IRAM_ATTR // fast
157+
bool expired()
158+
{
159+
YieldPolicyT::execute(); //in case of DoNothing: gets optimized away
160+
if(PeriodicT) //in case of false: gets optimized away
161+
return expiredRetrigger();
162+
return expiredOneShot();
163+
}
164+
165+
IRAM_ATTR // fast
166+
operator bool()
167+
{
168+
return expired();
169+
}
170+
171+
bool canExpire () const
172+
{
173+
return !_neverExpires;
174+
}
175+
176+
bool canWait () const
177+
{
178+
return _timeout != alwaysExpired;
179+
}
180+
181+
IRAM_ATTR // called from ISR
182+
void reset(const timeType newUserTimeout)
183+
{
184+
reset();
185+
_timeout = TimePolicyT::toTimeTypeUnit(newUserTimeout);
186+
_neverExpires = (newUserTimeout < 0) || (newUserTimeout > timeMax());
187+
}
188+
189+
IRAM_ATTR // called from ISR
190+
void reset()
191+
{
192+
_start = TimePolicyT::time();
193+
}
194+
195+
void resetToNeverExpires ()
196+
{
197+
_timeout = alwaysExpired + 1; // because canWait() has precedence
198+
_neverExpires = true;
199+
}
200+
201+
timeType getTimeout() const
202+
{
203+
return TimePolicyT::toUserUnit(_timeout);
204+
}
205+
206+
static constexpr timeType timeMax()
207+
{
208+
return TimePolicyT::timeMax;
209+
}
210+
211+
private:
212+
213+
IRAM_ATTR // fast
214+
bool checkExpired(const timeType internalUnit) const
215+
{
216+
// canWait() is not checked here
217+
// returns "can expire" and "time expired"
218+
return (!_neverExpires) && ((internalUnit - _start) >= _timeout);
219+
}
220+
221+
protected:
222+
223+
IRAM_ATTR // fast
224+
bool expiredRetrigger()
225+
{
226+
if (!canWait())
227+
return true;
228+
229+
timeType current = TimePolicyT::time();
230+
if(checkExpired(current))
231+
{
232+
unsigned long n = (current - _start) / _timeout; //how many _timeouts periods have elapsed, will usually be 1 (current - _start >= _timeout)
233+
_start += n * _timeout;
234+
return true;
235+
}
236+
return false;
237+
}
238+
239+
IRAM_ATTR // fast
240+
bool expiredOneShot() const
241+
{
242+
// returns "always expired" or "has expired"
243+
return !canWait() || checkExpired(TimePolicyT::time());
244+
}
245+
246+
timeType _timeout;
247+
timeType _start;
248+
bool _neverExpires;
249+
};
250+
251+
// legacy type names, deprecated (unit is milliseconds)
252+
253+
using oneShot = polledTimeout::timeoutTemplate<false> /*__attribute__((deprecated("use oneShotMs")))*/;
254+
using periodic = polledTimeout::timeoutTemplate<true> /*__attribute__((deprecated("use periodicMs")))*/;
255+
256+
// standard versions (based on millis())
257+
// timeMax() is 49.7 days ((2^32)-2 ms)
258+
259+
using oneShotMs = polledTimeout::timeoutTemplate<false>;
260+
using periodicMs = polledTimeout::timeoutTemplate<true>;
261+
262+
// Time policy based on ESP.getCycleCount(), and intended to be called very often:
263+
// "Fast" versions sacrifices time range for improved precision and reduced execution time (by 86%)
264+
// (cpu cycles for ::expired(): 372 (millis()) vs 52 (ESP.getCycleCount()))
265+
// timeMax() values:
266+
// Ms: max is 26843 ms (26.8 s)
267+
// Us: max is 26843545 us (26.8 s)
268+
// Ns: max is 1073741823 ns ( 1.07 s)
269+
// (time policy based on ESP.getCycleCount() is intended to be called very often)
270+
271+
using oneShotFastMs = polledTimeout::timeoutTemplate<false, YieldPolicy::DoNothing, TimePolicy::TimeFastMillis>;
272+
using periodicFastMs = polledTimeout::timeoutTemplate<true, YieldPolicy::DoNothing, TimePolicy::TimeFastMillis>;
273+
using oneShotFastUs = polledTimeout::timeoutTemplate<false, YieldPolicy::DoNothing, TimePolicy::TimeFastMicros>;
274+
using periodicFastUs = polledTimeout::timeoutTemplate<true, YieldPolicy::DoNothing, TimePolicy::TimeFastMicros>;
275+
using oneShotFastNs = polledTimeout::timeoutTemplate<false, YieldPolicy::DoNothing, TimePolicy::TimeFastNanos>;
276+
using periodicFastNs = polledTimeout::timeoutTemplate<true, YieldPolicy::DoNothing, TimePolicy::TimeFastNanos>;
277+
278+
} //polledTimeout
279+
280+
281+
/* A 1-shot timeout that auto-yields when in CONT can be built as follows:
282+
* using oneShotYieldMs = esp8266::polledTimeout::timeoutTemplate<false, esp8266::polledTimeout::YieldPolicy::YieldOrSkip>;
283+
*
284+
* Other policies can be implemented by the user, e.g.: simple yield that panics in SYS, and the polledTimeout types built as needed as shown above, without modifying this file.
285+
*/
286+
287+
}//esp8266
288+
289+
#endif

libraries/Schedule/library.properties

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
name=Schedule
2+
version=1.0
3+
author=
4+
maintainer=
5+
sentence=
6+
paragraph=
7+
category=Other
8+
url=
9+
architectures=esp32

0 commit comments

Comments
 (0)