An ESP32-based Redis-watcher and info-displayer https://rpjios.com
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.

270 lines
9.4 KiB

  1. #include "zw_provision.h"
  2. #include "zw_logging.h"
  3. #include "zw_wifi.h"
  4. #include "zw_redis.h"
  5. #include <WiFi.h>
  6. char *EEPROMCFG_WiFiSSID = NULL;
  7. char *EEPROMCFG_WiFiPass = NULL;
  8. char *EEPROMCFG_RedisHost = NULL;
  9. char *EEPROMCFG_RedisPass = NULL;
  10. char *EEPROMCFG_OTAHost = NULL;
  11. uint16_t EEPROMCFG_RedisPort = 0;
  12. String gHostname;
  13. char __hnShadowBuf[ZW_EEPROM_SIZE];
  14. void __debugClearEEPROM()
  15. {
  16. dprint("__debugClearEEPROM()\n");
  17. // not the quickest, but the most memory-efficient, algorithm
  18. for (int i = 0; i < EEPROM_SIZE; i++)
  19. EEPROM.write(i, 0);
  20. EEPROM.commit();
  21. }
  22. void EEPROM_setup()
  23. {
  24. EEPROM.begin(EEPROM_SIZE);
  25. }
  26. #define CFG_ELEMENTS 6
  27. #define CFG_HEADER_SIZE (CFG_ELEMENTS * sizeof(uint16_t))
  28. void CFG_EEPROM_read()
  29. {
  30. uint16_t lengths[CFG_ELEMENTS];
  31. dprint("CFG_EEPROM_read: reading %d for lengths\n", CFG_HEADER_SIZE);
  32. zwassert(EEPROM.readBytes(CFG_EEPROM_ADDR, lengths, CFG_HEADER_SIZE) == CFG_HEADER_SIZE);
  33. String lengthsStr = "";
  34. for (int i = 0; i < CFG_ELEMENTS; i++)
  35. {
  36. lengthsStr += String(lengths[i]) + " ";
  37. }
  38. dprint("CFG_EEPROM_read: LENGTHS: %s\n", lengthsStr.c_str());
  39. EEPROMCFG_WiFiSSID = (char *)malloc(lengths[0] + 1);
  40. bzero(EEPROMCFG_WiFiSSID, lengths[0] + 1);
  41. auto offset = CFG_EEPROM_ADDR + CFG_HEADER_SIZE;
  42. zwassert(EEPROM.readBytes(offset, EEPROMCFG_WiFiSSID, lengths[0]) == lengths[0]);
  43. offset += lengths[0];
  44. dprint("CFG_EEPROM_read: WIFI SSID: %s\n", EEPROMCFG_WiFiSSID);
  45. EEPROMCFG_WiFiPass = (char *)malloc(lengths[1] + 1);
  46. bzero(EEPROMCFG_WiFiPass, lengths[1] + 1);
  47. zwassert(EEPROM.readBytes(offset, EEPROMCFG_WiFiPass, lengths[1]) == lengths[1]);
  48. offset += lengths[1];
  49. dprint("CFG_EEPROM_read: WIFI PASS: %s\n", EEPROMCFG_WiFiPass);
  50. EEPROMCFG_RedisHost = (char *)malloc(lengths[2] + 1);
  51. bzero(EEPROMCFG_RedisHost, lengths[2] + 1);
  52. zwassert(EEPROM.readBytes(offset, EEPROMCFG_RedisHost, lengths[2]) == lengths[2]);
  53. offset += lengths[2];
  54. dprint("CFG_EEPROM_read: REDIS IP: %s\n", EEPROMCFG_RedisHost);
  55. zwassert(EEPROM.readBytes(offset, &EEPROMCFG_RedisPort, lengths[3]) == lengths[3]);
  56. offset += lengths[3];
  57. dprint("CFG_EEPROM_read: REDIS PORT: %d\n", EEPROMCFG_RedisPort);
  58. EEPROMCFG_RedisPass = (char *)malloc(lengths[4] + 1);
  59. bzero(EEPROMCFG_RedisPass, lengths[4] + 1);
  60. zwassert(EEPROM.readBytes(offset, EEPROMCFG_RedisPass, lengths[4]) == lengths[4]);
  61. offset += lengths[4];
  62. dprint("CFG_EEPROM_read: REDIS PASS: %s\n", EEPROMCFG_RedisPass);
  63. EEPROMCFG_OTAHost = (char *)malloc(lengths[5] + 1);
  64. bzero(EEPROMCFG_OTAHost, lengths[5] + 1);
  65. zwassert(EEPROM.readBytes(offset, EEPROMCFG_OTAHost, lengths[5]) == lengths[5]);
  66. offset += lengths[5];
  67. dprint("CFG_EEPROM_read: OTA HOST: %s\n", EEPROMCFG_OTAHost);
  68. }
  69. #define PSTRING_LENGTH_LIMIT (CFG_EEPROM_SIZE / 2)
  70. inline bool __provStrCheck(const char *str)
  71. {
  72. return str && strnlen(str, CFG_EEPROM_SIZE - CFG_HEADER_SIZE) <= PSTRING_LENGTH_LIMIT;
  73. }
  74. bool checkUnitProvisioning()
  75. {
  76. auto eeHnLength = EEPROM.readBytes(ZW_EEPROM_HOSTNAME_ADDR, __hnShadowBuf, ZW_EEPROM_SIZE);
  77. if (!eeHnLength || eeHnLength > ZW_EEPROM_SIZE)
  78. return false;
  79. gHostname = String(__hnShadowBuf);
  80. // it's possible for eeHnLength to be valid while the hostname itself
  81. // contains garbade data, in which case gHostname may have length
  82. // larger than the allowed size
  83. if (gHostname.length() > ZW_EEPROM_SIZE)
  84. return false;
  85. dprint("EEPROM HOSTNAME (%db, %db) %s\n", eeHnLength, gHostname.length(), gHostname.c_str());
  86. CFG_EEPROM_read();
  87. return __provStrCheck(EEPROMCFG_WiFiSSID) && __provStrCheck(EEPROMCFG_WiFiPass) &&
  88. __provStrCheck(EEPROMCFG_RedisHost) && __provStrCheck(EEPROMCFG_RedisPass) &&
  89. __provStrCheck(EEPROMCFG_OTAHost) && EEPROMCFG_RedisPort > 0;
  90. }
  91. #if ZEROWATCH_PROVISIONING_MODE
  92. void provisionUnit()
  93. {
  94. dprint("***** ZeroWatch provisioning mode *****\n");
  95. dprint("\tHostname: \t%s\n", ZWPROV_HOSTNAME);
  96. dprint("\tWiFi SSID: \t%s\n", ZWPROV_WIFI_SSID);
  97. dprint("\tWiFi password: \t%s\n", ZWPROV_WIFI_PASSWORD);
  98. dprint("\tRedis host: \t%s\n", ZWPROV_REDIS_HOST);
  99. dprint("\tRedis password: \t%s\n", ZWPROV_REDIS_PASSWORD);
  100. dprint("\tRedis port: \t%u\n", ZWPROV_REDIS_PORT);
  101. dprint("\tOTA host: \t%s\n", ZWPROV_OTA_HOST);
  102. dprint("***** WILL COMMIT THESE VALUES TO EEPROM IN %d SECONDS.... *****\n", ZWPROV_MODE_WRITE_DELAY);
  103. delay(ZWPROV_MODE_WRITE_DELAY * 1000);
  104. dprint("***** COMMITTING ABOVE VALUES TO EEPROM: *****\n");
  105. auto hostname = String(ZWPROV_HOSTNAME);
  106. zwassert(EEPROM.writeString(ZW_EEPROM_HOSTNAME_ADDR, hostname) == hostname.length());
  107. uint16_t lengths[CFG_ELEMENTS];
  108. auto _local_port = ZWPROV_REDIS_PORT;
  109. lengths[0] = strlen(ZWPROV_WIFI_SSID);
  110. lengths[1] = strlen(ZWPROV_WIFI_PASSWORD);
  111. lengths[2] = strlen(ZWPROV_REDIS_HOST);
  112. lengths[3] = sizeof(_local_port); // redis port, always 16 bits
  113. lengths[4] = strlen(ZWPROV_REDIS_PASSWORD);
  114. lengths[5] = strlen(ZWPROV_OTA_HOST);
  115. dprint("*** ZeroWatch provisioning: lengths: ");
  116. for (int i = 0; i < CFG_ELEMENTS; i++)
  117. {
  118. dprint("%d ", lengths[i]);
  119. }
  120. dprint("\n");
  121. auto writeLen = EEPROM.writeBytes(CFG_EEPROM_ADDR, lengths, CFG_HEADER_SIZE);
  122. if (writeLen != CFG_HEADER_SIZE)
  123. {
  124. dprint("*** ZeroWatch provisioning ERROR: Config write (%d) failed\n", writeLen);
  125. return;
  126. }
  127. auto offset = CFG_EEPROM_ADDR + CFG_HEADER_SIZE;
  128. void **ptrptrs = (void **)malloc(sizeof(void *) * CFG_ELEMENTS);
  129. ptrptrs[0] = (void *)ZWPROV_WIFI_SSID;
  130. ptrptrs[1] = (void *)ZWPROV_WIFI_PASSWORD;
  131. ptrptrs[2] = (void *)ZWPROV_REDIS_HOST;
  132. ptrptrs[3] = (void *)&_local_port;
  133. ptrptrs[4] = (void *)ZWPROV_REDIS_PASSWORD;
  134. ptrptrs[5] = (void *)ZWPROV_OTA_HOST;
  135. for (int i = 0; i < CFG_ELEMENTS; i++)
  136. {
  137. dprint("*** ZeroWatch provisioning: Writing %d bytes of element %d to address %d\n",
  138. lengths[i], i, offset);
  139. writeLen = EEPROM.writeBytes(offset, ptrptrs[i], lengths[i]);
  140. if (writeLen != lengths[i])
  141. {
  142. dprint("*** ZeroWatch provisioning ERROR: element %d failed to write (%d != %d)\n", i, writeLen, lengths[i]);
  143. return;
  144. }
  145. offset += lengths[i];
  146. }
  147. if (EEPROM.commit())
  148. {
  149. dprint("*** ZeroWatch provisioning: Write complete, verifying data...\n");
  150. if (checkUnitProvisioning())
  151. {
  152. auto hnVerify = EEPROM.readString(ZW_EEPROM_HOSTNAME_ADDR);
  153. zwassert(!memcmp(ZWPROV_HOSTNAME, hnVerify.c_str(), strlen(ZWPROV_HOSTNAME)));
  154. zwassert(!memcmp(ptrptrs[0], EEPROMCFG_WiFiSSID, lengths[0]));
  155. zwassert(!memcmp(ptrptrs[1], EEPROMCFG_WiFiPass, lengths[1]));
  156. zwassert(!memcmp(ptrptrs[2], EEPROMCFG_RedisHost, lengths[2]));
  157. zwassert(!memcmp(ptrptrs[3], &EEPROMCFG_RedisPort, lengths[3]));
  158. zwassert(!memcmp(ptrptrs[4], EEPROMCFG_RedisPass, lengths[4]));
  159. zwassert(!memcmp(ptrptrs[5], EEPROMCFG_OTAHost, lengths[5]));
  160. dprint("*** ZeroWatch provisioning: data verified correctly!\n");
  161. #if ZEROWATCH_PROVISIONING_OPTION_FUNCTIONAL_VERIFICATION
  162. dprint("*** ZeroWatch provisioning: starting functional check...\n");
  163. ZWAppConfig blankConfig;
  164. if (!zwWiFiInit(hnVerify.c_str(), blankConfig))
  165. {
  166. dprint("*** ZeroWatch provisioning: WiFi check failed\n");
  167. __haltOrCatchFire();
  168. }
  169. ZWRedisHostConfig redisConfig = {
  170. .host = EEPROMCFG_RedisHost,
  171. .port = EEPROMCFG_RedisPort,
  172. .password = EEPROMCFG_RedisPass};
  173. ZWRedis redisCheck(hnVerify, redisConfig);
  174. if (!redisCheck.connect())
  175. {
  176. dprint("*** ZeroWatch provisioning: Redis check failed\n");
  177. __haltOrCatchFire();
  178. }
  179. if (ZWPROV_FUNCTIONAL_VERIFICATION_REGISTRY_DEVICE_IN)
  180. {
  181. dprint("*** ZeroWatch provisioning: registering device in '%s'...\n",
  182. ZWPROV_FUNCTIONAL_VERIFICATION_REGISTRY_DEVICE_IN);
  183. if (!redisCheck.registerDevice(ZWPROV_FUNCTIONAL_VERIFICATION_REGISTRY_DEVICE_IN,
  184. ZWPROV_HOSTNAME, WiFi.macAddress().c_str()))
  185. {
  186. dprint("*** ZeroWatch provisioning WARNING: registration failed (or already registered)\n");
  187. }
  188. }
  189. #else
  190. dprint("*** ZeroWatch provisioning WARNING: functional verification will be SKIPPED\n");
  191. #endif
  192. dprint("\n\n***** ZeroWatch provisioning completed successfully!!\n");
  193. }
  194. else
  195. {
  196. dprint("*** ZeroWatch provisioning FAILED! :shrug:\n");
  197. }
  198. }
  199. else
  200. {
  201. dprint("*** ZeroWatch provisioning: commit failed\n");
  202. }
  203. __haltOrCatchFire();
  204. }
  205. #endif
  206. void verifyProvisioning()
  207. {
  208. EEPROM_setup();
  209. #if ZEROWATCH_DEL_PROVISIONS
  210. dprint("***** ZEROWATCH_DEL_PROVISIONS is set! Waiting %d seconds... *****\n", ZWPROV_MODE_WRITE_DELAY);
  211. delay(ZWPROV_MODE_WRITE_DELAY * 1000);
  212. dprint("***** CLEARING PROVISIONING!! ***** \n");
  213. __debugClearEEPROM();
  214. #endif
  215. #if ZEROWATCH_PROVISIONING_MODE
  216. provisionUnit();
  217. #endif
  218. if (!checkUnitProvisioning())
  219. {
  220. zlog("\n\nThis device is not provisioned! Please use ZEROWATCH_PROVISIONING_MODE to initialize it.");
  221. __haltOrCatchFire();
  222. }
  223. }