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.

577 lines
16 KiB

7 years ago
7 years ago
7 years ago
8 years ago
8 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
6 years ago
6 years ago
  1. /*
  2. SETTINGS MODULE
  3. Copyright (C) 2016-2018 by Xose Pérez <xose dot perez at gmail dot com>
  4. Module key prefix: cfg
  5. */
  6. #include <EEPROM_Rotate.h>
  7. #include <vector>
  8. #include "libs/EmbedisWrap.h"
  9. #include <Stream.h>
  10. #include "libs/StreamInjector.h"
  11. StreamInjector _serial = StreamInjector(TERMINAL_BUFFER_SIZE);
  12. EmbedisWrap embedis(_serial, TERMINAL_BUFFER_SIZE);
  13. bool _settings_save = false;
  14. std::vector<setting_key_check_callback_f> _setting_key_check_callbacks;
  15. // -----------------------------------------------------------------------------
  16. // Reverse engineering EEPROM storage format
  17. // -----------------------------------------------------------------------------
  18. unsigned long settingsSize() {
  19. unsigned pos = SPI_FLASH_SEC_SIZE - 1;
  20. while (size_t len = EEPROMr.read(pos)) {
  21. if (0xFF == len) break;
  22. pos = pos - len - 2;
  23. }
  24. return SPI_FLASH_SEC_SIZE - pos + EEPROM_DATA_END;
  25. }
  26. // -----------------------------------------------------------------------------
  27. bool settingsKeyExists(const char * key) {
  28. for (unsigned char i = 0; i < _setting_key_check_callbacks.size(); i++) {
  29. if ((_setting_key_check_callbacks[i])(key)) return true;
  30. }
  31. return false;
  32. }
  33. unsigned int settingsKeyCount() {
  34. unsigned count = 0;
  35. unsigned pos = SPI_FLASH_SEC_SIZE - 1;
  36. while (size_t len = EEPROMr.read(pos)) {
  37. if (0xFF == len) break;
  38. pos = pos - len - 2;
  39. len = EEPROMr.read(pos);
  40. pos = pos - len - 2;
  41. count ++;
  42. }
  43. return count;
  44. }
  45. String settingsKeyName(unsigned int index) {
  46. String s;
  47. unsigned count = 0;
  48. unsigned pos = SPI_FLASH_SEC_SIZE - 1;
  49. while (size_t len = EEPROMr.read(pos)) {
  50. if (0xFF == len) break;
  51. pos = pos - len - 2;
  52. if (count == index) {
  53. s.reserve(len);
  54. for (unsigned char i = 0 ; i < len; i++) {
  55. s += (char) EEPROMr.read(pos + i + 1);
  56. }
  57. break;
  58. }
  59. count++;
  60. len = EEPROMr.read(pos);
  61. pos = pos - len - 2;
  62. }
  63. return s;
  64. }
  65. std::vector<String> _settingsKeys() {
  66. // Get sorted list of keys
  67. std::vector<String> keys;
  68. //unsigned int size = settingsKeyCount();
  69. unsigned int size = settingsKeyCount();
  70. for (unsigned int i=0; i<size; i++) {
  71. //String key = settingsKeyName(i);
  72. String key = settingsKeyName(i);
  73. bool inserted = false;
  74. for (unsigned char j=0; j<keys.size(); j++) {
  75. // Check if we have to insert it before the current element
  76. if (keys[j].compareTo(key) > 0) {
  77. keys.insert(keys.begin() + j, key);
  78. inserted = true;
  79. break;
  80. }
  81. }
  82. // If we could not insert it, just push it at the end
  83. if (!inserted) keys.push_back(key);
  84. }
  85. return keys;
  86. }
  87. bool _settingsKeyCheck(const char * key) {
  88. if (strncmp(key, "admin", 5) == 0) return true;
  89. if (strncmp(key, "board", 5) == 0) return true;
  90. if (strncmp(key, "cfg", 3) == 0) return true;
  91. if (strncmp(key, "device", 6) == 0) return true;
  92. if (strncmp(key, "fw", 2) == 0) return true;
  93. if (strncmp(key, "hostname", 8) == 0) return true;
  94. if (strncmp(key, "loopDelay", 9) == 0) return true;
  95. if (strncmp(key, "wtfHeap", 7) == 0) return true;
  96. return false;
  97. }
  98. // -----------------------------------------------------------------------------
  99. // Commands
  100. // -----------------------------------------------------------------------------
  101. void _settingsHelpCommand() {
  102. // Get sorted list of commands
  103. std::vector<String> commands;
  104. unsigned char size = embedis.getCommandCount();
  105. for (unsigned int i=0; i<size; i++) {
  106. String command = embedis.getCommandName(i);
  107. bool inserted = false;
  108. for (unsigned char j=0; j<commands.size(); j++) {
  109. // Check if we have to insert it before the current element
  110. if (commands[j].compareTo(command) > 0) {
  111. commands.insert(commands.begin() + j, command);
  112. inserted = true;
  113. break;
  114. }
  115. }
  116. // If we could not insert it, just push it at the end
  117. if (!inserted) commands.push_back(command);
  118. }
  119. // Output the list
  120. DEBUG_MSG_P(PSTR("Available commands:\n"));
  121. for (unsigned char i=0; i<commands.size(); i++) {
  122. DEBUG_MSG_P(PSTR("> %s\n"), (commands[i]).c_str());
  123. }
  124. }
  125. void _settingsKeysCommand() {
  126. // Get sorted list of keys
  127. std::vector<String> keys = _settingsKeys();
  128. // Write key-values
  129. DEBUG_MSG_P(PSTR("Current settings:\n"));
  130. for (unsigned int i=0; i<keys.size(); i++) {
  131. String value = getSetting(keys[i]);
  132. DEBUG_MSG_P(PSTR("> %s => \"%s\"\n"), (keys[i]).c_str(), value.c_str());
  133. }
  134. unsigned long freeEEPROM = SPI_FLASH_SEC_SIZE - settingsSize();
  135. DEBUG_MSG_P(PSTR("Number of keys: %d\n"), keys.size());
  136. DEBUG_MSG_P(PSTR("Current EEPROM sector: %u\n"), EEPROMr.current());
  137. DEBUG_MSG_P(PSTR("Free EEPROM: %d bytes (%d%%)\n"), freeEEPROM, 100 * freeEEPROM / SPI_FLASH_SEC_SIZE);
  138. }
  139. void _settingsCleanCommand(bool do_delete) {
  140. // Get sorted list of keys
  141. std::vector<String> keys = _settingsKeys();
  142. unsigned int count = 0;
  143. for (unsigned int i=0; i<keys.size(); i++) {
  144. if (!settingsKeyExists((keys[i]).c_str())) {
  145. if (do_delete) {
  146. delSetting(keys[i]);
  147. DEBUG_MSG_P(PSTR("> %s deleted\n"), (keys[i]).c_str());
  148. } else {
  149. DEBUG_MSG_P(PSTR("> %s is safe to delete\n"), (keys[i]).c_str());
  150. }
  151. ++count;
  152. }
  153. }
  154. DEBUG_MSG_P(PSTR("%u unknown key(s) found\n"), count);
  155. }
  156. void _settingsFactoryResetCommand() {
  157. for (unsigned int i = 0; i < SPI_FLASH_SEC_SIZE; i++) {
  158. EEPROMr.write(i, 0xFF);
  159. }
  160. EEPROMr.commit();
  161. }
  162. void _settingsInitCommands() {
  163. #if DEBUG_SUPPORT
  164. settingsRegisterCommand(F("CRASH"), [](Embedis* e) {
  165. debugDumpCrashInfo();
  166. debugClearCrashInfo();
  167. DEBUG_MSG_P(PSTR("+OK\n"));
  168. });
  169. #endif
  170. settingsRegisterCommand(F("CLEAN"), [](Embedis* e) {
  171. bool do_delete = false;
  172. if (e->argc > 1) {
  173. do_delete = String(e->argv[1]).toInt() == 1;
  174. }
  175. _settingsCleanCommand(do_delete);
  176. DEBUG_MSG_P(PSTR("+OK\n"));
  177. });
  178. settingsRegisterCommand(F("COMMANDS"), [](Embedis* e) {
  179. _settingsHelpCommand();
  180. DEBUG_MSG_P(PSTR("+OK\n"));
  181. });
  182. settingsRegisterCommand(F("ERASE.CONFIG"), [](Embedis* e) {
  183. DEBUG_MSG_P(PSTR("+OK\n"));
  184. resetReason(CUSTOM_RESET_TERMINAL);
  185. ESP.eraseConfig();
  186. *((int*) 0) = 0; // see https://github.com/esp8266/Arduino/issues/1494
  187. });
  188. #if I2C_SUPPORT
  189. settingsRegisterCommand(F("I2C.SCAN"), [](Embedis* e) {
  190. i2cScan();
  191. DEBUG_MSG_P(PSTR("+OK\n"));
  192. });
  193. settingsRegisterCommand(F("I2C.CLEAR"), [](Embedis* e) {
  194. i2cClearBus();
  195. DEBUG_MSG_P(PSTR("+OK\n"));
  196. });
  197. #endif
  198. settingsRegisterCommand(F("FACTORY.RESET"), [](Embedis* e) {
  199. _settingsFactoryResetCommand();
  200. DEBUG_MSG_P(PSTR("+OK\n"));
  201. });
  202. settingsRegisterCommand(F("GPIO"), [](Embedis* e) {
  203. if (e->argc < 2) {
  204. DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n"));
  205. return;
  206. }
  207. int pin = String(e->argv[1]).toInt();
  208. //if (!gpioValid(pin)) {
  209. // DEBUG_MSG_P(PSTR("-ERROR: Invalid GPIO\n"));
  210. // return;
  211. //}
  212. if (e->argc > 2) {
  213. bool state = String(e->argv[2]).toInt() == 1;
  214. digitalWrite(pin, state);
  215. }
  216. DEBUG_MSG_P(PSTR("GPIO %d is %s\n"), pin, digitalRead(pin) == HIGH ? "HIGH" : "LOW");
  217. DEBUG_MSG_P(PSTR("+OK\n"));
  218. });
  219. settingsRegisterCommand(F("HEAP"), [](Embedis* e) {
  220. DEBUG_MSG_P(PSTR("Free HEAP: %d bytes\n"), getFreeHeap());
  221. DEBUG_MSG_P(PSTR("+OK\n"));
  222. });
  223. settingsRegisterCommand(F("HELP"), [](Embedis* e) {
  224. _settingsHelpCommand();
  225. DEBUG_MSG_P(PSTR("+OK\n"));
  226. });
  227. settingsRegisterCommand(F("INFO"), [](Embedis* e) {
  228. info();
  229. wifiDebug();
  230. //StreamString s;
  231. //WiFi.printDiag(s);
  232. //DEBUG_MSG(s.c_str());
  233. DEBUG_MSG_P(PSTR("+OK\n"));
  234. });
  235. settingsRegisterCommand(F("KEYS"), [](Embedis* e) {
  236. _settingsKeysCommand();
  237. DEBUG_MSG_P(PSTR("+OK\n"));
  238. });
  239. settingsRegisterCommand(F("GET"), [](Embedis* e) {
  240. if (e->argc < 2) {
  241. DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n"));
  242. return;
  243. }
  244. for (unsigned char i = 1; i < e->argc; i++) {
  245. String key = String(e->argv[i]);
  246. String value;
  247. if (!Embedis::get(key, value)) {
  248. DEBUG_MSG_P(PSTR("> %s =>\n"), key.c_str());
  249. continue;
  250. }
  251. DEBUG_MSG_P(PSTR("> %s => \"%s\"\n"), key.c_str(), value.c_str());
  252. }
  253. DEBUG_MSG_P(PSTR("+OK\n"));
  254. });
  255. settingsRegisterCommand(F("SET"), [](Embedis* e) {
  256. if (e->argc != 3) {
  257. DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n"));
  258. return;
  259. }
  260. String key = String(e->argv[1]);
  261. String value = String(e->argv[2]);
  262. // Hacks for backwards compatibility
  263. if (key.startsWith("ssid")) key.replace("ssid", "wifiName");
  264. if (key.startsWith("pass")) key.replace("pass", "wifiPass");
  265. if (Embedis::set(key, value)) {
  266. DEBUG_MSG_P(PSTR("+OK\n"));
  267. } else {
  268. DEBUG_MSG_P(PSTR("-ERROR: Error storing key-value\n"));
  269. }
  270. });
  271. #if WEB_SUPPORT
  272. settingsRegisterCommand(F("RELOAD"), [](Embedis* e) {
  273. wsReload();
  274. DEBUG_MSG_P(PSTR("+OK\n"));
  275. });
  276. #endif
  277. settingsRegisterCommand(F("RESET"), [](Embedis* e) {
  278. DEBUG_MSG_P(PSTR("+OK\n"));
  279. deferredReset(100, CUSTOM_RESET_TERMINAL);
  280. });
  281. settingsRegisterCommand(F("RESET.SAFE"), [](Embedis* e) {
  282. EEPROMr.write(EEPROM_CRASH_COUNTER, SYSTEM_CHECK_MAX);
  283. DEBUG_MSG_P(PSTR("+OK\n"));
  284. deferredReset(100, CUSTOM_RESET_TERMINAL);
  285. });
  286. settingsRegisterCommand(F("UPTIME"), [](Embedis* e) {
  287. DEBUG_MSG_P(PSTR("Uptime: %d seconds\n"), getUptime());
  288. DEBUG_MSG_P(PSTR("+OK\n"));
  289. });
  290. }
  291. // -----------------------------------------------------------------------------
  292. // Key-value API
  293. // -----------------------------------------------------------------------------
  294. void moveSetting(const char * from, const char * to) {
  295. String value = getSetting(from);
  296. if (value.length() > 0) setSetting(to, value);
  297. delSetting(from);
  298. }
  299. void moveSetting(const char * from, const char * to, unsigned int index) {
  300. String value = getSetting(from, index, "");
  301. if (value.length() > 0) setSetting(to, index, value);
  302. delSetting(from, index);
  303. }
  304. void moveSettings(const char * from, const char * to) {
  305. unsigned int index = 0;
  306. while (index < 100) {
  307. String value = getSetting(from, index, "");
  308. if (value.length() == 0) break;
  309. setSetting(to, index, value);
  310. delSetting(from, index);
  311. index++;
  312. }
  313. }
  314. template<typename T> String getSetting(const String& key, T defaultValue) {
  315. String value;
  316. if (!Embedis::get(key, value)) value = String(defaultValue);
  317. return value;
  318. }
  319. template<typename T> String getSetting(const String& key, unsigned int index, T defaultValue) {
  320. return getSetting(key + String(index), defaultValue);
  321. }
  322. String getSetting(const String& key) {
  323. return getSetting(key, "");
  324. }
  325. template<typename T> bool setSetting(const String& key, T value) {
  326. return Embedis::set(key, String(value));
  327. }
  328. template<typename T> bool setSetting(const String& key, unsigned int index, T value) {
  329. return setSetting(key + String(index), value);
  330. }
  331. bool delSetting(const String& key) {
  332. return Embedis::del(key);
  333. }
  334. bool delSetting(const String& key, unsigned int index) {
  335. return delSetting(key + String(index));
  336. }
  337. bool hasSetting(const String& key) {
  338. return getSetting(key).length() != 0;
  339. }
  340. bool hasSetting(const String& key, unsigned int index) {
  341. return getSetting(key, index, "").length() != 0;
  342. }
  343. void saveSettings() {
  344. #if not SETTINGS_AUTOSAVE
  345. _settings_save = true;
  346. #endif
  347. }
  348. void resetSettings() {
  349. _settingsFactoryResetCommand();
  350. }
  351. // -----------------------------------------------------------------------------
  352. // Settings
  353. // -----------------------------------------------------------------------------
  354. void settingsInject(void *data, size_t len) {
  355. _serial.inject((char *) data, len);
  356. }
  357. Stream & settingsSerial() {
  358. return (Stream &) _serial;
  359. }
  360. size_t settingsMaxSize() {
  361. size_t size = EEPROM_SIZE;
  362. if (size > SPI_FLASH_SEC_SIZE) size = SPI_FLASH_SEC_SIZE;
  363. size = (size + 3) & (~3);
  364. return size;
  365. }
  366. bool settingsRestoreJson(JsonObject& data) {
  367. // Check this is an ESPurna configuration file (must have "app":"ESPURNA")
  368. const char* app = data["app"];
  369. if (!app || strcmp(app, APP_NAME) != 0) {
  370. DEBUG_MSG_P(PSTR("[SETTING] Wrong or missing 'app' key\n"));
  371. return false;
  372. }
  373. // Clear settings
  374. bool is_backup = data["backup"];
  375. if (is_backup) {
  376. for (unsigned int i = EEPROM_DATA_END; i < SPI_FLASH_SEC_SIZE; i++) {
  377. EEPROMr.write(i, 0xFF);
  378. }
  379. }
  380. // Dump settings to memory buffer
  381. for (auto element : data) {
  382. if (strcmp(element.key, "app") == 0) continue;
  383. if (strcmp(element.key, "version") == 0) continue;
  384. if (strcmp(element.key, "backup") == 0) continue;
  385. if (strcmp(element.key, "timestamp") == 0) continue;
  386. setSetting(element.key, element.value.as<char*>());
  387. }
  388. // Persist to EEPROM
  389. saveSettings();
  390. DEBUG_MSG_P(PSTR("[SETTINGS] Settings restored successfully\n"));
  391. return true;
  392. }
  393. void settingsGetJson(JsonObject& root) {
  394. // Get sorted list of keys
  395. std::vector<String> keys = _settingsKeys();
  396. // Add the key-values to the json object
  397. for (unsigned int i=0; i<keys.size(); i++) {
  398. String value = getSetting(keys[i]);
  399. root[keys[i]] = value;
  400. }
  401. }
  402. void settingsRegisterCommand(const String& name, void (*call)(Embedis*)) {
  403. Embedis::command(name, call);
  404. };
  405. void settingsRegisterKeyCheck(setting_key_check_callback_f callback) {
  406. _setting_key_check_callbacks.push_back(callback);
  407. }
  408. // -----------------------------------------------------------------------------
  409. // Initialization
  410. // -----------------------------------------------------------------------------
  411. void settingsSetup() {
  412. _serial.callback([](uint8_t ch) {
  413. #if TELNET_SUPPORT
  414. telnetWrite(ch);
  415. #endif
  416. #if DEBUG_SERIAL_SUPPORT
  417. debugSerialWrite(ch);
  418. #endif
  419. });
  420. Embedis::dictionary( F("EEPROM"),
  421. SPI_FLASH_SEC_SIZE,
  422. [](size_t pos) -> char { return EEPROMr.read(pos); },
  423. [](size_t pos, char value) { EEPROMr.write(pos, value); },
  424. #if SETTINGS_AUTOSAVE
  425. []() { _settings_save = true; }
  426. #else
  427. []() {}
  428. #endif
  429. );
  430. _settingsInitCommands();
  431. // Register key check
  432. settingsRegisterKeyCheck(_settingsKeyCheck);
  433. // Register loop
  434. espurnaRegisterLoop(settingsLoop);
  435. }
  436. void settingsLoop() {
  437. if (_settings_save) {
  438. EEPROMr.commit();
  439. _settings_save = false;
  440. }
  441. #if TERMINAL_SUPPORT
  442. #if DEBUG_SERIAL_SUPPORT
  443. while (debugSerialAvailable()) {
  444. _serial.inject(debugSerialRead());
  445. }
  446. #endif
  447. embedis.process();
  448. #endif // TERMINAL_SUPPORT
  449. }