Fork of the espurna firmware for `mhsw` switches
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.

125 lines
4.1 KiB

Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
  1. /*
  2. OTA MODULE COMMON FUNCTIONS
  3. */
  4. #include "ota.h"
  5. #include "system.h"
  6. #include "terminal.h"
  7. #include "rtcmem.h"
  8. #include "utils.h"
  9. #include "ws.h"
  10. #include <atomic>
  11. void otaPrintError() {
  12. if (Update.hasError()) {
  13. #if TERMINAL_SUPPORT
  14. Update.printError(terminalDefaultStream());
  15. #elif DEBUG_SERIAL_SUPPORT && defined(DEBUG_PORT)
  16. Update.printError(DEBUG_PORT);
  17. #endif
  18. }
  19. }
  20. bool otaFinalize(size_t size, int reason, bool evenIfRemaining) {
  21. if (Update.isRunning() && Update.end(evenIfRemaining)) {
  22. DEBUG_MSG_P(PSTR("[OTA] Success: %7u bytes\n"), size);
  23. deferredReset(500, reason);
  24. return true;
  25. }
  26. otaPrintError();
  27. eepromRotate(true);
  28. return false;
  29. }
  30. // Helper methods from UpdaterClass that need to be called manually for async mode,
  31. // because we are not using Stream interface to feed it data.
  32. bool otaVerifyHeader(uint8_t* data, size_t len) {
  33. if (len < 4) {
  34. return false;
  35. }
  36. // ref: https://github.com/esp8266/Arduino/pull/6820
  37. // accept gzip, let unpacker figure things out later
  38. if (data[0] == 0x1F && data[1] == 0x8B) {
  39. return true;
  40. }
  41. // Check for magic byte with a normal .bin
  42. if (data[0] != 0xE9) {
  43. return false;
  44. }
  45. // Make sure that flash config can be recognized and fit the flash
  46. const auto flash_config = ESP.magicFlashChipSize((data[3] & 0xf0) >> 4);
  47. if (flash_config && (flash_config > ESP.getFlashChipRealSize())) {
  48. return false;
  49. }
  50. return true;
  51. }
  52. void otaProgress(size_t bytes, size_t each) {
  53. // Removed to avoid websocket ping back during upgrade (see #1574)
  54. // TODO: implement as separate from debugging message
  55. #if WEB_SUPPORT
  56. if (wsConnected()) return;
  57. #endif
  58. // Telnet and serial will still output things, but slightly throttled
  59. static size_t last = 0;
  60. if (bytes < last) {
  61. last = 0;
  62. }
  63. if ((bytes > each) && (bytes - each > last)) {
  64. DEBUG_MSG_P(PSTR("[OTA] Progress: %7u bytes\r"), bytes);
  65. last = bytes;
  66. }
  67. }
  68. void otaSetup() {
  69. // Some magic to allow seamless Tasmota OTA upgrades
  70. // - inject dummy data sequence that is expected to hold current version info
  71. // - purge settings, since we don't want accidentaly reading something as a kv
  72. // - sometimes we cannot boot b/c of certain SDK params, purge last 16KiB
  73. {
  74. // ref. `SetOption78 1`
  75. // - https://tasmota.github.io/docs/Commands/#setoptions (> SetOption78 Version check on Tasmota upgrade)
  76. // - https://github.com/esphome/esphome/blob/0e59243b83913fc724d0229514a84b6ea14717cc/esphome/core/esphal.cpp#L275-L287 (the original idea from esphome)
  77. // - https://github.com/arendst/Tasmota/blob/217addc2bb2cf46e7633c93e87954b245cb96556/tasmota/settings.ino#L218-L262 (specific checks, which succeed when finding 0xffffffff as version)
  78. // - https://github.com/arendst/Tasmota/blob/0dfa38df89c8f2a1e582d53d79243881645be0b8/tasmota/i18n.h#L780-L782 (constants)
  79. std::atomic_thread_fence(std::memory_order_relaxed);
  80. volatile uint32_t magic[3] [[gnu::unused]] {
  81. 0x5AA55AA5,
  82. 0xFFFFFFFF,
  83. 0xA55AA55A
  84. };
  85. // ref. https://github.com/arendst/Tasmota/blob/217addc2bb2cf46e7633c93e87954b245cb96556/tasmota/settings.ino#L24
  86. // We will certainly find these when rebooting from Tasmota. Purge SDK as well, since we may experience WDT after starting up the softAP
  87. auto* rtcmem = reinterpret_cast<volatile uint32_t*>(RTCMEM_ADDR);
  88. if ((0xA55A == rtcmem[64]) && (0xA55A == rtcmem[68])) {
  89. DEBUG_MSG_P(PSTR("[OTA] Detected TASMOTA OTA, resetting the device...\n"));
  90. rtcmem[64] = rtcmem[68] = 0;
  91. customResetReason(CUSTOM_RESET_TERMINAL);
  92. resetSettings();
  93. eraseSDKConfig();
  94. *((int*) 0) = 0;
  95. // noreturn, we simply reboot after writing into 0
  96. }
  97. // TODO: also check for things throughout the flash sector, somehow?
  98. }
  99. #if OTA_ARDUINOOTA_SUPPORT
  100. arduinoOtaSetup();
  101. #endif
  102. #if OTA_CLIENT != OTA_CLIENT_NONE
  103. otaClientSetup();
  104. #endif
  105. }