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.

450 lines
12 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. // on esp8266 this is a sntp timeshift'ed timestamp plus `micros64()`
  102. // resulting value is available from either
  103. // - `_gettimeofday_r(nullptr, &timeval_struct, nullptr);`, as both seconds and microseconds
  104. // - `std::time(...)` just as seconds
  105. //
  106. // notice that on boot it should be equal to the build timestamp when NTP_SUPPORT=1
  107. // (also, only works correctly with Cores >= 3, otherwise there are two different sources)
  108. struct RealtimeClock {
  109. using duration = std::chrono::duration<int64_t>;
  110. using rep = duration::rep;
  111. using period = duration::period;
  112. using time_point = std::chrono::time_point<RealtimeClock, duration>;
  113. static constexpr bool is_steady { false };
  114. static time_point now() noexcept {
  115. return time_point(duration(::std::time(nullptr)));
  116. }
  117. };
  118. // common 'Arduino Core' clock, fallback to 32bit and `millis()` to utilize certain math quirks
  119. // ref.
  120. // - https://github.com/esp8266/Arduino/issues/3078
  121. // - https://github.com/esp8266/Arduino/pull/4264
  122. struct CoreClock {
  123. using duration = espurna::duration::Milliseconds;
  124. using rep = duration::rep;
  125. using period = duration::period;
  126. using time_point = std::chrono::time_point<CoreClock, duration>;
  127. static constexpr bool is_steady { true };
  128. static time_point now() noexcept {
  129. return time_point(duration(::millis()));
  130. }
  131. };
  132. // Simple 'proxies' for most common operations
  133. inline SystemClock::time_point micros() {
  134. return SystemClock::now();
  135. }
  136. inline CoreClock::time_point millis() {
  137. return CoreClock::now();
  138. }
  139. // Attempt to sleep for N milliseconds, but this is allowed to be woken up at any point by the SDK
  140. inline void delay(CoreClock::duration value) {
  141. ::delay(value.count());
  142. }
  143. bool tryDelay(CoreClock::time_point start, CoreClock::duration timeout, CoreClock::duration interval);
  144. template <typename T>
  145. bool blockingDelay(CoreClock::duration timeout, CoreClock::duration interval, T&& blocked) {
  146. auto result = blocked();
  147. if (result) {
  148. const auto start = CoreClock::now();
  149. for (;;) {
  150. if (tryDelay(start, timeout, interval)) {
  151. break;
  152. }
  153. result = blocked();
  154. if (!result) {
  155. break;
  156. }
  157. }
  158. }
  159. return result;
  160. }
  161. // Local implementation of 'delay' that will make sure that we wait for the specified
  162. // time, even after being woken up. Allows to service Core tasks that are scheduled
  163. // in-between context switches, where the interval controls the minimum sleep time.
  164. bool blockingDelay(CoreClock::duration timeout, CoreClock::duration interval);
  165. bool blockingDelay(CoreClock::duration timeout);
  166. } // namespace time
  167. namespace timer {
  168. struct SystemTimer {
  169. using TimeSource = time::CoreClock;
  170. using Duration = TimeSource::duration;
  171. static constexpr Duration DurationMin = Duration(5);
  172. SystemTimer();
  173. ~SystemTimer() {
  174. stop();
  175. }
  176. SystemTimer(const SystemTimer&) = delete;
  177. SystemTimer& operator=(const SystemTimer&) = delete;
  178. SystemTimer(SystemTimer&&) = default;
  179. SystemTimer& operator=(SystemTimer&&) = default;
  180. explicit operator bool() const {
  181. return _armed != nullptr;
  182. }
  183. void once(Duration duration, Callback callback) {
  184. start(duration, std::move(callback), false);
  185. }
  186. void repeat(Duration duration, Callback callback) {
  187. start(duration, std::move(callback), true);
  188. }
  189. void schedule_once(Duration, Callback);
  190. void stop();
  191. private:
  192. // limit is per https://www.espressif.com/sites/default/files/documentation/2c-esp8266_non_os_sdk_api_reference_en.pdf
  193. // > 3.1.1 os_timer_arm
  194. // > with `system_timer_reinit()`, the timer value allowed ranges from 100 to 0x0x689D0.
  195. // > otherwise, the timer value allowed ranges from 5 to 0x68D7A3.
  196. // with current implementation we use division by 2 until we reach value less than this one
  197. static constexpr Duration DurationMax = Duration(6870947);
  198. void reset();
  199. void start(Duration, Callback, bool repeat);
  200. void callback();
  201. struct Tick {
  202. size_t total;
  203. size_t count;
  204. };
  205. Callback _callback;
  206. os_timer_t* _armed { nullptr };
  207. bool _repeat { false };
  208. std::unique_ptr<Tick> _tick;
  209. std::unique_ptr<os_timer_t> _timer;
  210. };
  211. } // namespace timer
  212. namespace heartbeat {
  213. using Mask = int32_t;
  214. using Callback = bool(*)(Mask);
  215. enum class Mode {
  216. None,
  217. Once,
  218. Repeat
  219. };
  220. enum class Report : Mask {
  221. Status = 1 << 1,
  222. Ssid = 1 << 2,
  223. Ip = 1 << 3,
  224. Mac = 1 << 4,
  225. Rssi = 1 << 5,
  226. Uptime = 1 << 6,
  227. Datetime = 1 << 7,
  228. Freeheap = 1 << 8,
  229. Vcc = 1 << 9,
  230. Relay = 1 << 10,
  231. Light = 1 << 11,
  232. Hostname = 1 << 12,
  233. App = 1 << 13,
  234. Version = 1 << 14,
  235. Board = 1 << 15,
  236. Loadavg = 1 << 16,
  237. Interval = 1 << 17,
  238. Description = 1 << 18,
  239. Range = 1 << 19,
  240. RemoteTemp = 1 << 20,
  241. Bssid = 1 << 21
  242. };
  243. constexpr Mask operator*(Report lhs, Mask rhs) {
  244. return static_cast<Mask>(lhs) * rhs;
  245. }
  246. constexpr Mask operator*(Mask lhs, Report rhs) {
  247. return lhs * static_cast<Mask>(rhs);
  248. }
  249. constexpr Mask operator|(Report lhs, Report rhs) {
  250. return static_cast<Mask>(lhs) | static_cast<Mask>(rhs);
  251. }
  252. constexpr Mask operator|(Report lhs, Mask rhs) {
  253. return static_cast<Mask>(lhs) | rhs;
  254. }
  255. constexpr Mask operator|(Mask lhs, Report rhs) {
  256. return lhs | static_cast<Mask>(rhs);
  257. }
  258. constexpr Mask operator&(Report lhs, Mask rhs) {
  259. return static_cast<Mask>(lhs) & rhs;
  260. }
  261. constexpr Mask operator&(Mask lhs, Report rhs) {
  262. return lhs & static_cast<Mask>(rhs);
  263. }
  264. constexpr Mask operator&(Report lhs, Report rhs) {
  265. return static_cast<Mask>(lhs) & static_cast<Mask>(rhs);
  266. }
  267. espurna::duration::Seconds currentInterval();
  268. espurna::duration::Milliseconds currentIntervalMs();
  269. Mask currentValue();
  270. Mode currentMode();
  271. } // namespace heartbeat
  272. namespace sleep {
  273. enum class Interrupt {
  274. Low,
  275. High,
  276. };
  277. } // namespace sleep
  278. namespace settings {
  279. namespace internal {
  280. template <>
  281. heartbeat::Mode convert(const String&);
  282. String serialize(heartbeat::Mode);
  283. String serialize(duration::Seconds);
  284. String serialize(duration::Milliseconds);
  285. String serialize(duration::ClockCycles);
  286. } // namespace internal
  287. } // namespace settings
  288. } // namespace espurna
  289. uint32_t randomNumber(uint32_t minimum, uint32_t maximum);
  290. uint32_t randomNumber();
  291. unsigned long systemFreeStack();
  292. HeapStats systemHeapStats();
  293. size_t systemFreeHeap();
  294. size_t systemInitialFreeHeap();
  295. bool eraseSDKConfig();
  296. void forceEraseSDKConfig();
  297. void factoryReset();
  298. uint32_t systemResetReason();
  299. uint8_t systemStabilityCounter();
  300. void systemStabilityCounter(uint8_t count);
  301. void systemForceStable();
  302. void systemForceUnstable();
  303. bool systemCheck();
  304. void customResetReason(CustomResetReason);
  305. CustomResetReason customResetReason();
  306. String customResetReasonToPayload(CustomResetReason);
  307. void deferredReset(espurna::duration::Milliseconds, CustomResetReason);
  308. void prepareReset(CustomResetReason);
  309. bool pendingDeferredReset();
  310. bool wakeupModemForcedSleep();
  311. bool prepareModemForcedSleep();
  312. using SleepCallback = void (*)();
  313. void systemBeforeSleep(SleepCallback);
  314. void systemAfterSleep(SleepCallback);
  315. bool instantLightSleep();
  316. bool instantLightSleep(espurna::sleep::Microseconds);
  317. bool instantLightSleep(uint8_t pin, espurna::sleep::Interrupt);
  318. bool instantDeepSleep(espurna::sleep::Microseconds);
  319. unsigned long systemLoadAverage();
  320. espurna::duration::Seconds systemHeartbeatInterval();
  321. void systemScheduleHeartbeat();
  322. void systemStopHeartbeat(espurna::heartbeat::Callback);
  323. void systemHeartbeat(espurna::heartbeat::Callback, espurna::heartbeat::Mode, espurna::duration::Seconds interval);
  324. void systemHeartbeat(espurna::heartbeat::Callback, espurna::heartbeat::Mode);
  325. void systemHeartbeat(espurna::heartbeat::Callback);
  326. bool systemHeartbeat();
  327. espurna::duration::Seconds systemUptime();
  328. espurna::StringView systemDevice();
  329. espurna::StringView systemIdentifier();
  330. espurna::StringView systemChipId();
  331. espurna::StringView systemShortChipId();
  332. espurna::StringView systemDefaultPassword();
  333. String systemPassword();
  334. bool systemPasswordEquals(espurna::StringView);
  335. String systemHostname();
  336. String systemDescription();
  337. void systemSetup();