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.

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