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.

232 lines
5.9 KiB

  1. /*
  2. ASYNC CLIENT OTA MODULE
  3. Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if OTA_CLIENT == OTA_CLIENT_ASYNCTCP
  6. // -----------------------------------------------------------------------------
  7. // Terminal OTA command
  8. // -----------------------------------------------------------------------------
  9. #if TERMINAL_SUPPORT || OTA_MQTT_SUPPORT
  10. #include <ESPAsyncTCP.h>
  11. #include "system.h"
  12. #include "libs/URL.h"
  13. std::unique_ptr<AsyncClient> _ota_client = nullptr;
  14. unsigned long _ota_size = 0;
  15. bool _ota_connected = false;
  16. std::unique_ptr<URL> _ota_url = nullptr;
  17. const char OTA_REQUEST_TEMPLATE[] PROGMEM =
  18. "GET %s HTTP/1.1\r\n"
  19. "Host: %s\r\n"
  20. "User-Agent: ESPurna\r\n"
  21. "Connection: close\r\n"
  22. "Content-Type: application/x-www-form-urlencoded\r\n"
  23. "Content-Length: 0\r\n\r\n\r\n";
  24. void _otaClientOnDisconnect(void *s, AsyncClient *c) {
  25. DEBUG_MSG_P(PSTR("\n"));
  26. if (Update.end(true)){
  27. DEBUG_MSG_P(PSTR("[OTA] Success: %u bytes\n"), _ota_size);
  28. deferredReset(100, CUSTOM_RESET_OTA);
  29. } else {
  30. #ifdef DEBUG_PORT
  31. Update.printError(DEBUG_PORT);
  32. #endif
  33. eepromRotate(true);
  34. }
  35. DEBUG_MSG_P(PSTR("[OTA] Disconnected\n"));
  36. _ota_connected = false;
  37. _ota_url = nullptr;
  38. _ota_client = nullptr;
  39. }
  40. void _otaClientOnTimeout(void *s, AsyncClient *c, uint32_t time) {
  41. _ota_connected = false;
  42. _ota_url = nullptr;
  43. _ota_client->close(true);
  44. }
  45. void _otaClientOnData(void * arg, AsyncClient * c, void * data, size_t len) {
  46. char * p = (char *) data;
  47. if (_ota_size == 0) {
  48. Update.runAsync(true);
  49. if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)) {
  50. #ifdef DEBUG_PORT
  51. Update.printError(DEBUG_PORT);
  52. #endif
  53. c->close(true);
  54. return;
  55. }
  56. p = strstr((char *)data, "\r\n\r\n") + 4;
  57. len = len - (p - (char *) data);
  58. }
  59. if (!Update.hasError()) {
  60. if (Update.write((uint8_t *) p, len) != len) {
  61. #ifdef DEBUG_PORT
  62. Update.printError(DEBUG_PORT);
  63. #endif
  64. c->close(true);
  65. return;
  66. }
  67. }
  68. _ota_size += len;
  69. DEBUG_MSG_P(PSTR("[OTA] Progress: %u bytes\r"), _ota_size);
  70. delay(0);
  71. }
  72. void _otaClientOnConnect(void *arg, AsyncClient *client) {
  73. #if ASYNC_TCP_SSL_ENABLED
  74. int check = getSetting("otaScCheck", OTA_SECURE_CLIENT_CHECK).toInt();
  75. if ((check == SECURE_CLIENT_CHECK_FINGERPRINT) && (443 == _ota_url->port)) {
  76. uint8_t fp[20] = {0};
  77. sslFingerPrintArray(getSetting("otaFP", OTA_FINGERPRINT).c_str(), fp);
  78. SSL * ssl = _ota_client->getSSL();
  79. if (ssl_match_fingerprint(ssl, fp) != SSL_OK) {
  80. DEBUG_MSG_P(PSTR("[OTA] Warning: certificate fingerpint doesn't match\n"));
  81. client->close(true);
  82. return;
  83. }
  84. }
  85. #endif
  86. // Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade
  87. eepromRotate(false);
  88. DEBUG_MSG_P(PSTR("[OTA] Downloading %s\n"), _ota_url->path.c_str());
  89. char buffer[strlen_P(OTA_REQUEST_TEMPLATE) + _ota_url->path.length() + _ota_url->host.length()];
  90. snprintf_P(buffer, sizeof(buffer), OTA_REQUEST_TEMPLATE, _ota_url->path.c_str(), _ota_url->host.c_str());
  91. client->write(buffer);
  92. }
  93. void _otaClientFrom(const String& url) {
  94. if (_ota_connected) {
  95. DEBUG_MSG_P(PSTR("[OTA] Already connected\n"));
  96. return;
  97. }
  98. _ota_size = 0;
  99. if (_ota_url) _ota_url = nullptr;
  100. _ota_url = std::make_unique<URL>(url);
  101. /*
  102. DEBUG_MSG_P(PSTR("[OTA] proto:%s host:%s port:%u path:%s\n"),
  103. _ota_url->protocol.c_str(),
  104. _ota_url->host.c_str(),
  105. _ota_url->port,
  106. _ota_url->path.c_str()
  107. );
  108. */
  109. // we only support HTTP
  110. if ((!_ota_url->protocol.equals("http")) && (!_ota_url->protocol.equals("https"))) {
  111. DEBUG_MSG_P(PSTR("[OTA] Incorrect URL specified\n"));
  112. _ota_url = nullptr;
  113. return;
  114. }
  115. if (!_ota_client) {
  116. _ota_client = std::make_unique<AsyncClient>();
  117. }
  118. _ota_client->onDisconnect(_otaClientOnDisconnect, nullptr);
  119. _ota_client->onTimeout(_otaClientOnTimeout, nullptr);
  120. _ota_client->onData(_otaClientOnData, nullptr);
  121. _ota_client->onConnect(_otaClientOnConnect, nullptr);
  122. #if ASYNC_TCP_SSL_ENABLED
  123. _ota_connected = _ota_client->connect(_ota_url->host.c_str(), _ota_url->port, 443 == _ota_url->port);
  124. #else
  125. _ota_connected = _ota_client->connect(_ota_url->host.c_str(), _ota_url->port);
  126. #endif
  127. if (!_ota_connected) {
  128. DEBUG_MSG_P(PSTR("[OTA] Connection failed\n"));
  129. _ota_url = nullptr;
  130. _ota_client->close(true);
  131. }
  132. }
  133. #endif // TERMINAL_SUPPORT || OTA_MQTT_SUPPORT
  134. #if TERMINAL_SUPPORT
  135. void _otaClientInitCommands() {
  136. terminalRegisterCommand(F("OTA"), [](Embedis* e) {
  137. if (e->argc < 2) {
  138. terminalError(F("OTA <url>"));
  139. } else {
  140. _otaClientFrom(String(e->argv[1]));
  141. terminalOK();
  142. }
  143. });
  144. }
  145. #endif // TERMINAL_SUPPORT
  146. #if OTA_MQTT_SUPPORT
  147. void _otaClientMqttCallback(unsigned int type, const char * topic, const char * payload) {
  148. if (type == MQTT_CONNECT_EVENT) {
  149. mqttSubscribe(MQTT_TOPIC_OTA);
  150. }
  151. if (type == MQTT_MESSAGE_EVENT) {
  152. String t = mqttMagnitude((char *) topic);
  153. if (t.equals(MQTT_TOPIC_OTA)) {
  154. DEBUG_MSG_P(PSTR("[OTA] Initiating from URL: %s\n"), payload);
  155. _otaClientFrom(payload);
  156. }
  157. }
  158. }
  159. #endif // OTA_MQTT_SUPPORT
  160. // -----------------------------------------------------------------------------
  161. void otaClientSetup() {
  162. // Backwards compatibility
  163. moveSetting("otafp", "otaFP");
  164. #if TERMINAL_SUPPORT
  165. _otaClientInitCommands();
  166. #endif
  167. #if (MQTT_SUPPORT && OTA_MQTT_SUPPORT)
  168. mqttRegister(_otaClientMqttCallback);
  169. #endif
  170. }
  171. #endif // OTA_CLIENT == OTA_CLIENT_ASYNCTCP