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.

447 lines
12 KiB

6 years ago
7 years ago
6 years ago
6 years ago
8 years ago
8 years ago
7 years ago
8 years ago
8 years ago
8 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 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. */
  5. #include <EEPROM.h>
  6. #include <vector>
  7. #include "libs/EmbedisWrap.h"
  8. #include <Stream.h>
  9. #ifdef DEBUG_PORT
  10. #define EMBEDIS_PORT DEBUG_PORT
  11. #else
  12. #define EMBEDIS_PORT Serial
  13. #endif
  14. #if TELNET_SUPPORT
  15. #include "libs/StreamInjector.h"
  16. StreamInjector _serial = StreamInjector(EMBEDIS_PORT, TERMINAL_BUFFER_SIZE);
  17. #undef EMBEDIS_PORT
  18. #define EMBEDIS_PORT _serial
  19. #endif
  20. EmbedisWrap embedis(EMBEDIS_PORT, TERMINAL_BUFFER_SIZE);
  21. #if TERMINAL_SUPPORT
  22. #if SERIAL_RX_ENABLED
  23. char _serial_rx_buffer[TERMINAL_BUFFER_SIZE];
  24. static unsigned char _serial_rx_pointer = 0;
  25. #endif // SERIAL_RX_ENABLED
  26. #endif // TERMINAL_SUPPORT
  27. bool _settings_save = false;
  28. // -----------------------------------------------------------------------------
  29. // Reverse engineering EEPROM storage format
  30. // -----------------------------------------------------------------------------
  31. unsigned long settingsSize() {
  32. unsigned pos = SPI_FLASH_SEC_SIZE - 1;
  33. while (size_t len = EEPROM.read(pos)) {
  34. pos = pos - len - 2;
  35. }
  36. return SPI_FLASH_SEC_SIZE - pos;
  37. }
  38. // -----------------------------------------------------------------------------
  39. unsigned int _settingsKeyCount() {
  40. unsigned count = 0;
  41. unsigned pos = SPI_FLASH_SEC_SIZE - 1;
  42. while (size_t len = EEPROM.read(pos)) {
  43. pos = pos - len - 2;
  44. len = EEPROM.read(pos);
  45. pos = pos - len - 2;
  46. count ++;
  47. }
  48. return count;
  49. }
  50. String _settingsKeyName(unsigned int index) {
  51. String s;
  52. unsigned count = 0;
  53. unsigned pos = SPI_FLASH_SEC_SIZE - 1;
  54. while (size_t len = EEPROM.read(pos)) {
  55. pos = pos - len - 2;
  56. if (count == index) {
  57. s.reserve(len);
  58. for (unsigned char i = 0 ; i < len; i++) {
  59. s += (char) EEPROM.read(pos + i + 1);
  60. }
  61. break;
  62. }
  63. count++;
  64. len = EEPROM.read(pos);
  65. pos = pos - len - 2;
  66. }
  67. return s;
  68. }
  69. // -----------------------------------------------------------------------------
  70. // Commands
  71. // -----------------------------------------------------------------------------
  72. void _settingsHelp() {
  73. // Get sorted list of commands
  74. std::vector<String> commands;
  75. unsigned char size = embedis.getCommandCount();
  76. for (unsigned int i=0; i<size; i++) {
  77. String command = embedis.getCommandName(i);
  78. bool inserted = false;
  79. for (unsigned char j=0; j<commands.size(); j++) {
  80. // Check if we have to insert it before the current element
  81. if (commands[j].compareTo(command) > 0) {
  82. commands.insert(commands.begin() + j, command);
  83. inserted = true;
  84. break;
  85. }
  86. }
  87. // If we could not insert it, just push it at the end
  88. if (!inserted) commands.push_back(command);
  89. }
  90. // Output the list
  91. DEBUG_MSG_P(PSTR("Available commands:\n"));
  92. for (unsigned char i=0; i<commands.size(); i++) {
  93. DEBUG_MSG_P(PSTR("> %s\n"), (commands[i]).c_str());
  94. }
  95. }
  96. void _settingsKeys() {
  97. // Get sorted list of keys
  98. std::vector<String> keys;
  99. //unsigned int size = settingsKeyCount();
  100. unsigned int size = _settingsKeyCount();
  101. for (unsigned int i=0; i<size; i++) {
  102. //String key = settingsKeyName(i);
  103. String key = _settingsKeyName(i);
  104. bool inserted = false;
  105. for (unsigned char j=0; j<keys.size(); j++) {
  106. // Check if we have to insert it before the current element
  107. if (keys[j].compareTo(key) > 0) {
  108. keys.insert(keys.begin() + j, key);
  109. inserted = true;
  110. break;
  111. }
  112. }
  113. // If we could not insert it, just push it at the end
  114. if (!inserted) keys.push_back(key);
  115. }
  116. // Write key-values
  117. DEBUG_MSG_P(PSTR("Current settings:\n"));
  118. for (unsigned int i=0; i<keys.size(); i++) {
  119. String value = getSetting(keys[i]);
  120. DEBUG_MSG_P(PSTR("> %s => %s\n"), (keys[i]).c_str(), value.c_str());
  121. }
  122. unsigned long freeEEPROM = SPI_FLASH_SEC_SIZE - settingsSize();
  123. DEBUG_MSG_P(PSTR("Number of keys: %d\n"), keys.size());
  124. DEBUG_MSG_P(PSTR("Free EEPROM: %d bytes (%d%%)\n"), freeEEPROM, 100 * freeEEPROM / SPI_FLASH_SEC_SIZE);
  125. }
  126. void _settingsFactoryReset() {
  127. for (unsigned int i = 0; i < SPI_FLASH_SEC_SIZE; i++) {
  128. EEPROM.write(i, 0xFF);
  129. }
  130. EEPROM.commit();
  131. }
  132. void _settingsDump(bool ascii) {
  133. for (unsigned int i = 0; i < SPI_FLASH_SEC_SIZE; i++) {
  134. if (i % 16 == 0) DEBUG_MSG_P(PSTR("\n[%04X] "), i);
  135. byte c = EEPROM.read(i);
  136. if (ascii && 32 <= c && c <= 126) {
  137. DEBUG_MSG_P(PSTR(" %c "), c);
  138. } else {
  139. DEBUG_MSG_P(PSTR("%02X "), c);
  140. }
  141. }
  142. }
  143. void _settingsInitCommands() {
  144. #if DEBUG_SUPPORT
  145. settingsRegisterCommand(F("CRASH"), [](Embedis* e) {
  146. debugDumpCrashInfo();
  147. DEBUG_MSG_P(PSTR("+OK\n"));
  148. });
  149. #endif
  150. settingsRegisterCommand(F("COMMANDS"), [](Embedis* e) {
  151. _settingsHelp();
  152. DEBUG_MSG_P(PSTR("+OK\n"));
  153. });
  154. settingsRegisterCommand(F("EEPROM.DUMP"), [](Embedis* e) {
  155. bool ascii = false;
  156. if (e->argc == 2) ascii = String(e->argv[1]).toInt() == 1;
  157. _settingsDump(ascii);
  158. DEBUG_MSG_P(PSTR("\n+OK\n"));
  159. });
  160. settingsRegisterCommand(F("ERASE.CONFIG"), [](Embedis* e) {
  161. DEBUG_MSG_P(PSTR("+OK\n"));
  162. resetReason(CUSTOM_RESET_TERMINAL);
  163. ESP.eraseConfig();
  164. *((int*) 0) = 0; // see https://github.com/esp8266/Arduino/issues/1494
  165. });
  166. settingsRegisterCommand(F("FACTORY.RESET"), [](Embedis* e) {
  167. _settingsFactoryReset();
  168. DEBUG_MSG_P(PSTR("+OK\n"));
  169. });
  170. settingsRegisterCommand(F("GPIO"), [](Embedis* e) {
  171. if (e->argc < 2) {
  172. DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n"));
  173. return;
  174. }
  175. int pin = String(e->argv[1]).toInt();
  176. //if (!gpioValid(pin)) {
  177. // DEBUG_MSG_P(PSTR("-ERROR: Invalid GPIO\n"));
  178. // return;
  179. //}
  180. if (e->argc > 2) {
  181. bool state = String(e->argv[2]).toInt() == 1;
  182. digitalWrite(pin, state);
  183. }
  184. DEBUG_MSG_P(PSTR("GPIO %d is %s\n"), pin, digitalRead(pin) == HIGH ? "HIGH" : "LOW");
  185. DEBUG_MSG_P(PSTR("+OK\n"));
  186. });
  187. settingsRegisterCommand(F("HEAP"), [](Embedis* e) {
  188. DEBUG_MSG_P(PSTR("Free HEAP: %d bytes\n"), getFreeHeap());
  189. DEBUG_MSG_P(PSTR("+OK\n"));
  190. });
  191. settingsRegisterCommand(F("HELP"), [](Embedis* e) {
  192. _settingsHelp();
  193. DEBUG_MSG_P(PSTR("+OK\n"));
  194. });
  195. settingsRegisterCommand(F("INFO"), [](Embedis* e) {
  196. info();
  197. wifiStatus();
  198. //StreamString s;
  199. //WiFi.printDiag(s);
  200. //DEBUG_MSG(s.c_str());
  201. DEBUG_MSG_P(PSTR("+OK\n"));
  202. });
  203. settingsRegisterCommand(F("KEYS"), [](Embedis* e) {
  204. _settingsKeys();
  205. DEBUG_MSG_P(PSTR("+OK\n"));
  206. });
  207. settingsRegisterCommand(F("RESET"), [](Embedis* e) {
  208. DEBUG_MSG_P(PSTR("+OK\n"));
  209. deferredReset(100, CUSTOM_RESET_TERMINAL);
  210. });
  211. settingsRegisterCommand(F("UPTIME"), [](Embedis* e) {
  212. DEBUG_MSG_P(PSTR("Uptime: %d seconds\n"), getUptime());
  213. DEBUG_MSG_P(PSTR("+OK\n"));
  214. });
  215. }
  216. // -----------------------------------------------------------------------------
  217. // Key-value API
  218. // -----------------------------------------------------------------------------
  219. void moveSetting(const char * from, const char * to) {
  220. String value = getSetting(from);
  221. if (value.length() > 0) setSetting(to, value);
  222. delSetting(from);
  223. }
  224. template<typename T> String getSetting(const String& key, T defaultValue) {
  225. String value;
  226. if (!Embedis::get(key, value)) value = String(defaultValue);
  227. return value;
  228. }
  229. template<typename T> String getSetting(const String& key, unsigned int index, T defaultValue) {
  230. return getSetting(key + String(index), defaultValue);
  231. }
  232. String getSetting(const String& key) {
  233. return getSetting(key, "");
  234. }
  235. template<typename T> bool setSetting(const String& key, T value) {
  236. return Embedis::set(key, String(value));
  237. }
  238. template<typename T> bool setSetting(const String& key, unsigned int index, T value) {
  239. return setSetting(key + String(index), value);
  240. }
  241. bool delSetting(const String& key) {
  242. return Embedis::del(key);
  243. }
  244. bool delSetting(const String& key, unsigned int index) {
  245. return delSetting(key + String(index));
  246. }
  247. bool hasSetting(const String& key) {
  248. return getSetting(key).length() != 0;
  249. }
  250. bool hasSetting(const String& key, unsigned int index) {
  251. return getSetting(key, index, "").length() != 0;
  252. }
  253. void saveSettings() {
  254. #if not SETTINGS_AUTOSAVE
  255. _settings_save = true;
  256. #endif
  257. }
  258. void resetSettings() {
  259. _settingsFactoryReset();
  260. }
  261. // -----------------------------------------------------------------------------
  262. // Settings
  263. // -----------------------------------------------------------------------------
  264. #if TELNET_SUPPORT
  265. void settingsInject(void *data, size_t len) {
  266. _serial.inject((char *) data, len);
  267. }
  268. #endif
  269. size_t settingsMaxSize() {
  270. size_t size = EEPROM_SIZE;
  271. if (size > SPI_FLASH_SEC_SIZE) size = SPI_FLASH_SEC_SIZE;
  272. size = (size + 3) & (~3);
  273. return size;
  274. }
  275. bool settingsRestoreJson(JsonObject& data) {
  276. const char* app = data["app"];
  277. if (strcmp(app, APP_NAME) != 0) return false;
  278. for (unsigned int i = EEPROM_DATA_END; i < SPI_FLASH_SEC_SIZE; i++) {
  279. EEPROM.write(i, 0xFF);
  280. }
  281. for (auto element : data) {
  282. if (strcmp(element.key, "app") == 0) continue;
  283. if (strcmp(element.key, "version") == 0) continue;
  284. setSetting(element.key, element.value.as<char*>());
  285. }
  286. saveSettings();
  287. DEBUG_MSG_P(PSTR("[SETTINGS] Settings restored successfully\n"));
  288. return true;
  289. }
  290. bool settingsGetJson(JsonObject& root) {
  291. unsigned int size = _settingsKeyCount();
  292. for (unsigned int i=0; i<size; i++) {
  293. String key = _settingsKeyName(i);
  294. String value = getSetting(key);
  295. root[key] = value;
  296. }
  297. }
  298. void settingsRegisterCommand(const String& name, void (*call)(Embedis*)) {
  299. Embedis::command(name, call);
  300. };
  301. // -----------------------------------------------------------------------------
  302. // Initialization
  303. // -----------------------------------------------------------------------------
  304. void settingsSetup() {
  305. EEPROM.begin(SPI_FLASH_SEC_SIZE);
  306. #if TELNET_SUPPORT
  307. _serial.callback([](uint8_t ch) {
  308. telnetWrite(ch);
  309. });
  310. #endif
  311. Embedis::dictionary( F("EEPROM"),
  312. SPI_FLASH_SEC_SIZE,
  313. [](size_t pos) -> char { return EEPROM.read(pos); },
  314. [](size_t pos, char value) { EEPROM.write(pos, value); },
  315. #if SETTINGS_AUTOSAVE
  316. []() { _settings_save = true; }
  317. #else
  318. []() {}
  319. #endif
  320. );
  321. _settingsInitCommands();
  322. #if TERMINAL_SUPPORT
  323. #if SERIAL_RX_ENABLED
  324. SERIAL_RX_PORT.begin(SERIAL_RX_BAUDRATE);
  325. #endif // SERIAL_RX_ENABLED
  326. #endif // TERMINAL_SUPPORT
  327. // Register loop
  328. espurnaRegisterLoop(settingsLoop);
  329. }
  330. void settingsLoop() {
  331. if (_settings_save) {
  332. EEPROM.commit();
  333. _settings_save = false;
  334. }
  335. #if TERMINAL_SUPPORT
  336. embedis.process();
  337. #if SERIAL_RX_ENABLED
  338. while (SERIAL_RX_PORT.available() > 0) {
  339. char rc = Serial.read();
  340. _serial_rx_buffer[_serial_rx_pointer++] = rc;
  341. if ((_serial_rx_pointer == TERMINAL_BUFFER_SIZE) || (rc == 10)) {
  342. settingsInject(_serial_rx_buffer, (size_t) _serial_rx_pointer);
  343. _serial_rx_pointer = 0;
  344. }
  345. }
  346. #endif // SERIAL_RX_ENABLED
  347. #endif // TERMINAL_SUPPORT
  348. }