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.

595 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. infoMemory("Heap", getInitialFreeHeap(), getFreeHeap());
  221. DEBUG_MSG_P(PSTR("+OK\n"));
  222. });
  223. settingsRegisterCommand(F("STACK"), [](Embedis* e) {
  224. infoMemory("Stack", 4096, getFreeStack());
  225. DEBUG_MSG_P(PSTR("+OK\n"));
  226. });
  227. settingsRegisterCommand(F("HELP"), [](Embedis* e) {
  228. _settingsHelpCommand();
  229. DEBUG_MSG_P(PSTR("+OK\n"));
  230. });
  231. settingsRegisterCommand(F("INFO"), [](Embedis* e) {
  232. info();
  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. espurnaReload();
  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. settingsRegisterCommand(F("CONFIG"), [](Embedis* e) {
  291. DynamicJsonBuffer jsonBuffer;
  292. JsonObject& root = jsonBuffer.createObject();
  293. settingsGetJson(root);
  294. String output;
  295. root.printTo(output);
  296. DEBUG_MSG(output.c_str());
  297. DEBUG_MSG_P(PSTR("\n+OK\n"));
  298. });
  299. #if not SETTINGS_AUTOSAVE
  300. settingsRegisterCommand(F("SAVE"), [](Embedis* e) {
  301. _settings_save = true;
  302. DEBUG_MSG_P(PSTR("\n+OK\n"));
  303. });
  304. #endif
  305. }
  306. // -----------------------------------------------------------------------------
  307. // Key-value API
  308. // -----------------------------------------------------------------------------
  309. void moveSetting(const char * from, const char * to) {
  310. String value = getSetting(from);
  311. if (value.length() > 0) setSetting(to, value);
  312. delSetting(from);
  313. }
  314. void moveSetting(const char * from, const char * to, unsigned int index) {
  315. String value = getSetting(from, index, "");
  316. if (value.length() > 0) setSetting(to, index, value);
  317. delSetting(from, index);
  318. }
  319. void moveSettings(const char * from, const char * to) {
  320. unsigned int index = 0;
  321. while (index < 100) {
  322. String value = getSetting(from, index, "");
  323. if (value.length() == 0) break;
  324. setSetting(to, index, value);
  325. delSetting(from, index);
  326. index++;
  327. }
  328. }
  329. template<typename T> String getSetting(const String& key, T defaultValue) {
  330. String value;
  331. if (!Embedis::get(key, value)) value = String(defaultValue);
  332. return value;
  333. }
  334. template<typename T> String getSetting(const String& key, unsigned int index, T defaultValue) {
  335. return getSetting(key + String(index), defaultValue);
  336. }
  337. String getSetting(const String& key) {
  338. return getSetting(key, "");
  339. }
  340. template<typename T> bool setSetting(const String& key, T value) {
  341. return Embedis::set(key, String(value));
  342. }
  343. template<typename T> bool setSetting(const String& key, unsigned int index, T value) {
  344. return setSetting(key + String(index), value);
  345. }
  346. bool delSetting(const String& key) {
  347. return Embedis::del(key);
  348. }
  349. bool delSetting(const String& key, unsigned int index) {
  350. return delSetting(key + String(index));
  351. }
  352. bool hasSetting(const String& key) {
  353. return getSetting(key).length() != 0;
  354. }
  355. bool hasSetting(const String& key, unsigned int index) {
  356. return getSetting(key, index, "").length() != 0;
  357. }
  358. void saveSettings() {
  359. #if not SETTINGS_AUTOSAVE
  360. _settings_save = true;
  361. #endif
  362. }
  363. void resetSettings() {
  364. _settingsFactoryResetCommand();
  365. }
  366. // -----------------------------------------------------------------------------
  367. // Settings
  368. // -----------------------------------------------------------------------------
  369. void settingsInject(void *data, size_t len) {
  370. _serial.inject((char *) data, len);
  371. }
  372. Stream & settingsSerial() {
  373. return (Stream &) _serial;
  374. }
  375. size_t settingsMaxSize() {
  376. size_t size = EEPROM_SIZE;
  377. if (size > SPI_FLASH_SEC_SIZE) size = SPI_FLASH_SEC_SIZE;
  378. size = (size + 3) & (~3);
  379. return size;
  380. }
  381. bool settingsRestoreJson(JsonObject& data) {
  382. // Check this is an ESPurna configuration file (must have "app":"ESPURNA")
  383. const char* app = data["app"];
  384. if (!app || strcmp(app, APP_NAME) != 0) {
  385. DEBUG_MSG_P(PSTR("[SETTING] Wrong or missing 'app' key\n"));
  386. return false;
  387. }
  388. // Clear settings
  389. bool is_backup = data["backup"];
  390. if (is_backup) {
  391. for (unsigned int i = EEPROM_DATA_END; i < SPI_FLASH_SEC_SIZE; i++) {
  392. EEPROMr.write(i, 0xFF);
  393. }
  394. }
  395. // Dump settings to memory buffer
  396. for (auto element : data) {
  397. if (strcmp(element.key, "app") == 0) continue;
  398. if (strcmp(element.key, "version") == 0) continue;
  399. if (strcmp(element.key, "backup") == 0) continue;
  400. if (strcmp(element.key, "timestamp") == 0) continue;
  401. setSetting(element.key, element.value.as<char*>());
  402. }
  403. // Persist to EEPROM
  404. saveSettings();
  405. DEBUG_MSG_P(PSTR("[SETTINGS] Settings restored successfully\n"));
  406. return true;
  407. }
  408. void settingsGetJson(JsonObject& root) {
  409. // Get sorted list of keys
  410. std::vector<String> keys = _settingsKeys();
  411. // Add the key-values to the json object
  412. for (unsigned int i=0; i<keys.size(); i++) {
  413. String value = getSetting(keys[i]);
  414. root[keys[i]] = value;
  415. }
  416. }
  417. void settingsRegisterCommand(const String& name, void (*call)(Embedis*)) {
  418. Embedis::command(name, call);
  419. };
  420. void settingsRegisterKeyCheck(setting_key_check_callback_f callback) {
  421. _setting_key_check_callbacks.push_back(callback);
  422. }
  423. // -----------------------------------------------------------------------------
  424. // Initialization
  425. // -----------------------------------------------------------------------------
  426. void settingsSetup() {
  427. _serial.callback([](uint8_t ch) {
  428. #if TELNET_SUPPORT
  429. telnetWrite(ch);
  430. #endif
  431. #if DEBUG_SERIAL_SUPPORT
  432. debugSerialWrite(ch);
  433. #endif
  434. });
  435. Embedis::dictionary( F("EEPROM"),
  436. SPI_FLASH_SEC_SIZE,
  437. [](size_t pos) -> char { return EEPROMr.read(pos); },
  438. [](size_t pos, char value) { EEPROMr.write(pos, value); },
  439. #if SETTINGS_AUTOSAVE
  440. []() { _settings_save = true; }
  441. #else
  442. []() {}
  443. #endif
  444. );
  445. _settingsInitCommands();
  446. // Register key check
  447. settingsRegisterKeyCheck(_settingsKeyCheck);
  448. // Register loop
  449. espurnaRegisterLoop(settingsLoop);
  450. }
  451. void settingsLoop() {
  452. if (_settings_save) {
  453. EEPROMr.commit();
  454. _settings_save = false;
  455. }
  456. #if TERMINAL_SUPPORT
  457. #if DEBUG_SERIAL_SUPPORT
  458. while (debugSerialAvailable()) {
  459. _serial.inject(debugSerialRead());
  460. }
  461. #endif
  462. embedis.process();
  463. #endif // TERMINAL_SUPPORT
  464. }