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.

312 lines
8.1 KiB

  1. /*
  2. TERMINAL MODULE
  3. Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if TERMINAL_SUPPORT
  6. #include <vector>
  7. #include "libs/EmbedisWrap.h"
  8. #include <Stream.h>
  9. #include "libs/StreamInjector.h"
  10. #include "libs/HeapStats.h"
  11. StreamInjector _serial = StreamInjector(TERMINAL_BUFFER_SIZE);
  12. EmbedisWrap embedis(_serial, TERMINAL_BUFFER_SIZE);
  13. #if SERIAL_RX_ENABLED
  14. char _serial_rx_buffer[TERMINAL_BUFFER_SIZE];
  15. static unsigned char _serial_rx_pointer = 0;
  16. #endif // SERIAL_RX_ENABLED
  17. // -----------------------------------------------------------------------------
  18. // Commands
  19. // -----------------------------------------------------------------------------
  20. void _terminalHelpCommand() {
  21. // Get sorted list of commands
  22. std::vector<String> commands;
  23. unsigned char size = embedis.getCommandCount();
  24. for (unsigned int i=0; i<size; i++) {
  25. String command = embedis.getCommandName(i);
  26. bool inserted = false;
  27. for (unsigned char j=0; j<commands.size(); j++) {
  28. // Check if we have to insert it before the current element
  29. if (commands[j].compareTo(command) > 0) {
  30. commands.insert(commands.begin() + j, command);
  31. inserted = true;
  32. break;
  33. }
  34. }
  35. // If we could not insert it, just push it at the end
  36. if (!inserted) commands.push_back(command);
  37. }
  38. // Output the list
  39. DEBUG_MSG_P(PSTR("Available commands:\n"));
  40. for (unsigned char i=0; i<commands.size(); i++) {
  41. DEBUG_MSG_P(PSTR("> %s\n"), (commands[i]).c_str());
  42. }
  43. }
  44. void _terminalKeysCommand() {
  45. // Get sorted list of keys
  46. std::vector<String> keys = _settingsKeys();
  47. // Write key-values
  48. DEBUG_MSG_P(PSTR("Current settings:\n"));
  49. for (unsigned int i=0; i<keys.size(); i++) {
  50. String value = getSetting(keys[i]);
  51. DEBUG_MSG_P(PSTR("> %s => \"%s\"\n"), (keys[i]).c_str(), value.c_str());
  52. }
  53. unsigned long freeEEPROM = SPI_FLASH_SEC_SIZE - settingsSize();
  54. UNUSED(freeEEPROM);
  55. DEBUG_MSG_P(PSTR("Number of keys: %d\n"), keys.size());
  56. DEBUG_MSG_P(PSTR("Current EEPROM sector: %u\n"), EEPROMr.current());
  57. DEBUG_MSG_P(PSTR("Free EEPROM: %d bytes (%d%%)\n"), freeEEPROM, 100 * freeEEPROM / SPI_FLASH_SEC_SIZE);
  58. }
  59. void _terminalInitCommand() {
  60. terminalRegisterCommand(F("COMMANDS"), [](Embedis* e) {
  61. _terminalHelpCommand();
  62. terminalOK();
  63. });
  64. terminalRegisterCommand(F("ERASE.CONFIG"), [](Embedis* e) {
  65. terminalOK();
  66. customResetReason(CUSTOM_RESET_TERMINAL);
  67. eraseSDKConfig();
  68. *((int*) 0) = 0; // see https://github.com/esp8266/Arduino/issues/1494
  69. });
  70. terminalRegisterCommand(F("FACTORY.RESET"), [](Embedis* e) {
  71. resetSettings();
  72. terminalOK();
  73. });
  74. terminalRegisterCommand(F("GPIO"), [](Embedis* e) {
  75. int pin = -1;
  76. if (e->argc < 2) {
  77. DEBUG_MSG("Printing all GPIO pins:\n");
  78. } else {
  79. pin = String(e->argv[1]).toInt();
  80. if (!gpioValid(pin)) {
  81. terminalError(F("Invalid GPIO pin"));
  82. return;
  83. }
  84. if (e->argc > 2) {
  85. bool state = String(e->argv[2]).toInt() == 1;
  86. digitalWrite(pin, state);
  87. }
  88. }
  89. for (int i = 0; i <= 15; i++) {
  90. if (gpioValid(i) && (pin == -1 || pin == i)) {
  91. DEBUG_MSG_P(PSTR("GPIO %s pin %d is %s\n"), GPEP(i) ? "output" : "input", i, digitalRead(i) == HIGH ? "HIGH" : "LOW");
  92. }
  93. }
  94. terminalOK();
  95. });
  96. terminalRegisterCommand(F("HEAP"), [](Embedis* e) {
  97. infoHeapStats();
  98. terminalOK();
  99. });
  100. terminalRegisterCommand(F("STACK"), [](Embedis* e) {
  101. infoMemory("Stack", CONT_STACKSIZE, getFreeStack());
  102. terminalOK();
  103. });
  104. terminalRegisterCommand(F("HELP"), [](Embedis* e) {
  105. _terminalHelpCommand();
  106. terminalOK();
  107. });
  108. terminalRegisterCommand(F("INFO"), [](Embedis* e) {
  109. info();
  110. terminalOK();
  111. });
  112. terminalRegisterCommand(F("KEYS"), [](Embedis* e) {
  113. _terminalKeysCommand();
  114. terminalOK();
  115. });
  116. terminalRegisterCommand(F("GET"), [](Embedis* e) {
  117. if (e->argc < 2) {
  118. terminalError(F("Wrong arguments"));
  119. return;
  120. }
  121. for (unsigned char i = 1; i < e->argc; i++) {
  122. String key = String(e->argv[i]);
  123. String value;
  124. if (!Embedis::get(key, value)) {
  125. DEBUG_MSG_P(PSTR("> %s =>\n"), key.c_str());
  126. continue;
  127. }
  128. DEBUG_MSG_P(PSTR("> %s => \"%s\"\n"), key.c_str(), value.c_str());
  129. }
  130. terminalOK();
  131. });
  132. terminalRegisterCommand(F("RELOAD"), [](Embedis* e) {
  133. espurnaReload();
  134. terminalOK();
  135. });
  136. terminalRegisterCommand(F("RESET"), [](Embedis* e) {
  137. terminalOK();
  138. deferredReset(100, CUSTOM_RESET_TERMINAL);
  139. });
  140. terminalRegisterCommand(F("RESET.SAFE"), [](Embedis* e) {
  141. systemStabilityCounter(SYSTEM_CHECK_MAX);
  142. terminalOK();
  143. deferredReset(100, CUSTOM_RESET_TERMINAL);
  144. });
  145. terminalRegisterCommand(F("UPTIME"), [](Embedis* e) {
  146. DEBUG_MSG_P(PSTR("Uptime: %d seconds\n"), getUptime());
  147. terminalOK();
  148. });
  149. terminalRegisterCommand(F("CONFIG"), [](Embedis* e) {
  150. DynamicJsonBuffer jsonBuffer(1024);
  151. JsonObject& root = jsonBuffer.createObject();
  152. settingsGetJson(root);
  153. // XXX: replace with streaming
  154. String output;
  155. root.printTo(output);
  156. DEBUG_MSG(output.c_str());
  157. });
  158. #if not SETTINGS_AUTOSAVE
  159. terminalRegisterCommand(F("SAVE"), [](Embedis* e) {
  160. eepromCommit();
  161. terminalOK();
  162. });
  163. #endif
  164. #if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
  165. terminalRegisterCommand(F("MFLN.PROBE"), [](Embedis* e) {
  166. if (e->argc != 3) {
  167. terminalError(F("[url] [value]"));
  168. return;
  169. }
  170. URL _url(e->argv[1]);
  171. uint16_t requested_mfln = atol(e->argv[2]);
  172. auto client = std::make_unique<BearSSL::WiFiClientSecure>();
  173. client->setInsecure();
  174. if (client->probeMaxFragmentLength(_url.host.c_str(), _url.port, requested_mfln)) {
  175. terminalOK();
  176. } else {
  177. terminalError(F("Buffer size not supported"));
  178. }
  179. });
  180. #endif
  181. }
  182. void _terminalLoop() {
  183. #if DEBUG_SERIAL_SUPPORT
  184. while (DEBUG_PORT.available()) {
  185. _serial.inject(DEBUG_PORT.read());
  186. }
  187. #endif
  188. embedis.process();
  189. #if SERIAL_RX_ENABLED
  190. while (SERIAL_RX_PORT.available() > 0) {
  191. char rc = SERIAL_RX_PORT.read();
  192. _serial_rx_buffer[_serial_rx_pointer++] = rc;
  193. if ((_serial_rx_pointer == TERMINAL_BUFFER_SIZE) || (rc == 10)) {
  194. terminalInject(_serial_rx_buffer, (size_t) _serial_rx_pointer);
  195. _serial_rx_pointer = 0;
  196. }
  197. }
  198. #endif // SERIAL_RX_ENABLED
  199. }
  200. // -----------------------------------------------------------------------------
  201. // Pubic API
  202. // -----------------------------------------------------------------------------
  203. void terminalInject(void *data, size_t len) {
  204. _serial.inject((char *) data, len);
  205. }
  206. void terminalInject(char ch) {
  207. _serial.inject(ch);
  208. }
  209. Stream & terminalSerial() {
  210. return (Stream &) _serial;
  211. }
  212. void terminalRegisterCommand(const String& name, void (*call)(Embedis*)) {
  213. Embedis::command(name, call);
  214. };
  215. void terminalOK() {
  216. DEBUG_MSG_P(PSTR("+OK\n"));
  217. }
  218. void terminalError(const String& error) {
  219. DEBUG_MSG_P(PSTR("-ERROR: %s\n"), error.c_str());
  220. }
  221. void terminalSetup() {
  222. _serial.callback([](uint8_t ch) {
  223. #if TELNET_SUPPORT
  224. telnetWrite(ch);
  225. #endif
  226. #if DEBUG_SERIAL_SUPPORT
  227. DEBUG_PORT.write(ch);
  228. #endif
  229. });
  230. _terminalInitCommand();
  231. #if SERIAL_RX_ENABLED
  232. SERIAL_RX_PORT.begin(SERIAL_RX_BAUDRATE);
  233. #endif // SERIAL_RX_ENABLED
  234. // Register loop
  235. espurnaRegisterLoop(_terminalLoop);
  236. }
  237. #endif // TERMINAL_SUPPORT