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.

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