Mirror of espurna firmware for wireless switches and more
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

454 lines
11 KiB

1 year ago
1 year ago
  1. /*
  2. SYSTEM MODULE
  3. Copyright (C) 2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #pragma once
  6. #include "settings.h"
  7. #include "types.h"
  8. #include <chrono>
  9. #include <cstdint>
  10. #include <limits>
  11. #include <user_interface.h>
  12. struct HeapStats {
  13. uint32_t available;
  14. uint32_t usable;
  15. uint8_t fragmentation;
  16. };
  17. enum class CustomResetReason : uint8_t {
  18. None,
  19. Button, // button event action
  20. Factory, // requested factory reset
  21. Hardware, // driver event
  22. Mqtt,
  23. Ota, // successful ota
  24. Rpc, // rpc (api) calls
  25. Rule, // rpn rule operator action
  26. Scheduler, // scheduled reset
  27. Terminal, // terminal command action
  28. Web, // webui action
  29. Stability, // stable counter action
  30. };
  31. namespace espurna {
  32. namespace sleep {
  33. // Both LIGHT and DEEP sleep accept microseconds as input
  34. // Effective limit is ~31bit - 1 in size
  35. using Microseconds = std::chrono::duration<uint32_t, std::micro>;
  36. constexpr auto FpmSleepMin = Microseconds{ 1000 };
  37. constexpr auto FpmSleepIndefinite = Microseconds{ 0xFFFFFFF };
  38. } // namespace sleep
  39. namespace system {
  40. struct RandomDevice {
  41. using result_type = uint32_t;
  42. static constexpr result_type min() {
  43. return std::numeric_limits<result_type>::min();
  44. }
  45. static constexpr result_type max() {
  46. return std::numeric_limits<result_type>::max();
  47. }
  48. uint32_t operator()() const;
  49. };
  50. } // namespace system
  51. namespace duration {
  52. // TODO: cpu frequency value might not always be true at build-time, detect at boot instead?
  53. // (also notice the discrepancy when OTA'ing between different values, as CPU *may* keep the old value)
  54. using ClockCycles = std::chrono::duration<uint32_t, std::ratio<1, F_CPU>>;
  55. namespace critical {
  56. using Microseconds = std::chrono::duration<uint16_t, std::micro>;
  57. } // namespace critical
  58. } // namespace duration
  59. namespace time {
  60. namespace critical {
  61. // Wait for the specified amount of time *without* using SDK or Core timers.
  62. // Supposedly, should be the same as a simple do-while loop.
  63. inline void delay(duration::critical::Microseconds) __attribute__((always_inline));
  64. inline void delay(duration::critical::Microseconds duration) {
  65. ::ets_delay_us(duration.count());
  66. }
  67. } // namespace critical
  68. struct CpuClock {
  69. using duration = espurna::duration::ClockCycles;
  70. using rep = duration::rep;
  71. using period = duration::period;
  72. using time_point = std::chrono::time_point<CpuClock, duration>;
  73. static constexpr bool is_steady { true };
  74. // `"rsr %0, ccount\n" : "=a" (out) :: "memory"` on xtensa
  75. // or "soc_get_ccount()" with esp8266-idf
  76. // or "cpu_hal_get_cycle_count()" with esp-idf
  77. // (and notably, every one of them is 32bit)
  78. static time_point now() noexcept {
  79. return time_point(duration(::esp_get_cycle_count()));
  80. }
  81. };
  82. inline CpuClock::time_point ccount() {
  83. return CpuClock::now();
  84. }
  85. // chrono's system_clock and steady_clock are implemented in the libstdc++
  86. // at the time of writing this, `steady_clock::now()` *is* `system_clock::now()`
  87. // (aka `std::time(nullptr)` aka `clock_gettime(CLOCK_REALTIME, ...)`)
  88. //
  89. // notice that the `micros()` by itself relies on `system_get_time()` which uses 32bit
  90. // storage (...or slightly less that that) and will overflow at around 72 minute mark.
  91. struct SystemClock {
  92. using duration = espurna::duration::Microseconds;
  93. using rep = duration::rep;
  94. using period = duration::period;
  95. using time_point = std::chrono::time_point<SystemClock, duration>;
  96. static constexpr bool is_steady { true };
  97. static time_point now() noexcept {
  98. return time_point(duration(::micros64()));
  99. }
  100. };
  101. // common 'Arduino Core' clock, fallback to 32bit and `millis()` to utilize certain math quirks
  102. // ref.
  103. // - https://github.com/esp8266/Arduino/issues/3078
  104. // - https://github.com/esp8266/Arduino/pull/4264
  105. struct CoreClock {
  106. using duration = espurna::duration::Milliseconds;
  107. using rep = duration::rep;
  108. using period = duration::period;
  109. using time_point = std::chrono::time_point<CoreClock, duration>;
  110. static constexpr bool is_steady { true };
  111. static time_point now() noexcept {
  112. return time_point(duration(::millis()));
  113. }
  114. };
  115. // Simple 'proxies' for most common operations
  116. inline SystemClock::time_point micros() {
  117. return SystemClock::now();
  118. }
  119. inline CoreClock::time_point millis() {
  120. return CoreClock::now();
  121. }
  122. // Attempt to sleep for N milliseconds, but this is allowed to be woken up at any point by the SDK
  123. inline void delay(CoreClock::duration value) {
  124. ::delay(value.count());
  125. }
  126. bool tryDelay(CoreClock::time_point start, CoreClock::duration timeout, CoreClock::duration interval);
  127. template <typename T>
  128. bool blockingDelay(CoreClock::duration timeout, CoreClock::duration interval, T&& blocked) {
  129. auto result = blocked();
  130. if (result) {
  131. const auto start = CoreClock::now();
  132. for (;;) {
  133. if (tryDelay(start, timeout, interval)) {
  134. break;
  135. }
  136. result = blocked();
  137. if (!result) {
  138. break;
  139. }
  140. }
  141. }
  142. return result;
  143. }
  144. // Local implementation of 'delay' that will make sure that we wait for the specified
  145. // time, even after being woken up. Allows to service Core tasks that are scheduled
  146. // in-between context switches, where the interval controls the minimum sleep time.
  147. bool blockingDelay(CoreClock::duration timeout, CoreClock::duration interval);
  148. bool blockingDelay(CoreClock::duration timeout);
  149. } // namespace time
  150. namespace timer {
  151. struct SystemTimer {
  152. using TimeSource = time::CoreClock;
  153. using Duration = TimeSource::duration;
  154. static constexpr Duration DurationMin = Duration(5);
  155. SystemTimer();
  156. ~SystemTimer() {
  157. stop();
  158. }
  159. SystemTimer(const SystemTimer&) = delete;
  160. SystemTimer& operator=(const SystemTimer&) = delete;
  161. SystemTimer(SystemTimer&&) = default;
  162. SystemTimer& operator=(SystemTimer&&) = default;
  163. bool armed() const {
  164. return _armed != nullptr;
  165. }
  166. explicit operator bool() const {
  167. return armed();
  168. }
  169. void once(Duration duration, Callback callback) {
  170. start(duration, std::move(callback), false);
  171. }
  172. void repeat(Duration duration, Callback callback) {
  173. start(duration, std::move(callback), true);
  174. }
  175. void schedule_once(Duration, Callback);
  176. void stop();
  177. private:
  178. // limit is per https://www.espressif.com/sites/default/files/documentation/2c-esp8266_non_os_sdk_api_reference_en.pdf
  179. // > 3.1.1 os_timer_arm
  180. // > with `system_timer_reinit()`, the timer value allowed ranges from 100 to 0x0x689D0.
  181. // > otherwise, the timer value allowed ranges from 5 to 0x68D7A3.
  182. // with current implementation we use division by 2 until we reach value less than this one
  183. static constexpr Duration DurationMax = Duration(6870947);
  184. void reset();
  185. void start(Duration, Callback, bool repeat);
  186. void callback();
  187. struct Tick {
  188. size_t total;
  189. size_t count;
  190. };
  191. Callback _callback;
  192. os_timer_t* _armed { nullptr };
  193. bool _repeat { false };
  194. std::unique_ptr<Tick> _tick;
  195. std::unique_ptr<os_timer_t> _timer;
  196. };
  197. } // namespace timer
  198. struct ReadyFlag {
  199. bool wait(duration::Milliseconds);
  200. void stop();
  201. bool stop_wait(duration::Milliseconds duration) {
  202. stop();
  203. return wait(duration);
  204. }
  205. bool ready() const {
  206. return _ready;
  207. }
  208. explicit operator bool() const {
  209. return ready();
  210. }
  211. private:
  212. bool _ready { true };
  213. timer::SystemTimer _timer;
  214. };
  215. namespace heartbeat {
  216. using Mask = int32_t;
  217. using Callback = bool(*)(Mask);
  218. enum class Mode {
  219. None,
  220. Once,
  221. Repeat
  222. };
  223. enum class Report : Mask {
  224. Status = 1 << 1,
  225. Ssid = 1 << 2,
  226. Ip = 1 << 3,
  227. Mac = 1 << 4,
  228. Rssi = 1 << 5,
  229. Uptime = 1 << 6,
  230. Datetime = 1 << 7,
  231. Freeheap = 1 << 8,
  232. Vcc = 1 << 9,
  233. Relay = 1 << 10,
  234. Light = 1 << 11,
  235. Hostname = 1 << 12,
  236. App = 1 << 13,
  237. Version = 1 << 14,
  238. Board = 1 << 15,
  239. Loadavg = 1 << 16,
  240. Interval = 1 << 17,
  241. Description = 1 << 18,
  242. Range = 1 << 19,
  243. RemoteTemp = 1 << 20,
  244. Bssid = 1 << 21
  245. };
  246. constexpr Mask operator*(Report lhs, Mask rhs) {
  247. return static_cast<Mask>(lhs) * rhs;
  248. }
  249. constexpr Mask operator*(Mask lhs, Report rhs) {
  250. return lhs * static_cast<Mask>(rhs);
  251. }
  252. constexpr Mask operator|(Report lhs, Report rhs) {
  253. return static_cast<Mask>(lhs) | static_cast<Mask>(rhs);
  254. }
  255. constexpr Mask operator|(Report lhs, Mask rhs) {
  256. return static_cast<Mask>(lhs) | rhs;
  257. }
  258. constexpr Mask operator|(Mask lhs, Report rhs) {
  259. return lhs | static_cast<Mask>(rhs);
  260. }
  261. constexpr Mask operator&(Report lhs, Mask rhs) {
  262. return static_cast<Mask>(lhs) & rhs;
  263. }
  264. constexpr Mask operator&(Mask lhs, Report rhs) {
  265. return lhs & static_cast<Mask>(rhs);
  266. }
  267. constexpr Mask operator&(Report lhs, Report rhs) {
  268. return static_cast<Mask>(lhs) & static_cast<Mask>(rhs);
  269. }
  270. espurna::duration::Seconds currentInterval();
  271. espurna::duration::Milliseconds currentIntervalMs();
  272. Mask currentValue();
  273. Mode currentMode();
  274. } // namespace heartbeat
  275. namespace sleep {
  276. enum class Interrupt {
  277. Low,
  278. High,
  279. };
  280. } // namespace sleep
  281. namespace settings {
  282. namespace internal {
  283. template <>
  284. heartbeat::Mode convert(const String&);
  285. String serialize(heartbeat::Mode);
  286. String serialize(duration::ClockCycles);
  287. } // namespace internal
  288. } // namespace settings
  289. } // namespace espurna
  290. uint32_t randomNumber(uint32_t minimum, uint32_t maximum);
  291. uint32_t randomNumber();
  292. unsigned long systemFreeStack();
  293. HeapStats systemHeapStats();
  294. size_t systemFreeHeap();
  295. size_t systemInitialFreeHeap();
  296. bool eraseSDKConfig();
  297. void forceEraseSDKConfig();
  298. void factoryReset();
  299. uint32_t systemResetReason();
  300. uint8_t systemStabilityCounter();
  301. void systemStabilityCounter(uint8_t count);
  302. void systemForceStable();
  303. void systemForceUnstable();
  304. bool systemCheck();
  305. void customResetReason(CustomResetReason);
  306. CustomResetReason customResetReason();
  307. String customResetReasonToPayload(CustomResetReason);
  308. void deferredReset(espurna::duration::Milliseconds, CustomResetReason);
  309. void prepareReset(CustomResetReason);
  310. bool pendingDeferredReset();
  311. bool wakeupModemForcedSleep();
  312. bool prepareModemForcedSleep();
  313. using SleepCallback = void (*)();
  314. void systemBeforeSleep(SleepCallback);
  315. void systemAfterSleep(SleepCallback);
  316. bool instantLightSleep();
  317. bool instantLightSleep(espurna::sleep::Microseconds);
  318. bool instantLightSleep(uint8_t pin, espurna::sleep::Interrupt);
  319. bool instantDeepSleep(espurna::sleep::Microseconds);
  320. unsigned long systemLoadAverage();
  321. espurna::duration::Seconds systemHeartbeatInterval();
  322. void systemScheduleHeartbeat();
  323. void systemStopHeartbeat(espurna::heartbeat::Callback);
  324. void systemHeartbeat(espurna::heartbeat::Callback, espurna::heartbeat::Mode, espurna::duration::Seconds interval);
  325. void systemHeartbeat(espurna::heartbeat::Callback, espurna::heartbeat::Mode);
  326. void systemHeartbeat(espurna::heartbeat::Callback);
  327. bool systemHeartbeat();
  328. espurna::duration::Seconds systemUptime();
  329. espurna::StringView systemDevice();
  330. espurna::StringView systemIdentifier();
  331. espurna::StringView systemChipId();
  332. espurna::StringView systemShortChipId();
  333. espurna::StringView systemDefaultPassword();
  334. String systemPassword();
  335. bool systemPasswordEquals(espurna::StringView);
  336. String systemHostname();
  337. String systemDescription();
  338. void systemSetup();