DIY fertilizer mixer and plant watering machine https://www.xythobuz.de/giessomat.html
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.

Statemachine.cpp 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  1. /*
  2. * Copyright (c) 2021 Thomas Buck <thomas@xythobuz.de>
  3. *
  4. * This file is part of Giess-o-mat.
  5. *
  6. * Giess-o-mat is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Giess-o-mat is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Giess-o-mat. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. #include "Plants.h"
  20. #include "DebugLog.h"
  21. #include "WifiStuff.h"
  22. #include "Statemachine.h"
  23. #include "config.h"
  24. Statemachine::DigitBuffer::DigitBuffer(int _size) {
  25. size = _size;
  26. pos = 0;
  27. digits = new int[size];
  28. }
  29. Statemachine::DigitBuffer::~DigitBuffer() {
  30. delete digits;
  31. }
  32. bool Statemachine::DigitBuffer::spaceLeft(void) {
  33. return (pos < size);
  34. }
  35. bool Statemachine::DigitBuffer::hasDigits(void) {
  36. return (pos > 0);
  37. }
  38. int Statemachine::DigitBuffer::countDigits(void) {
  39. return pos;
  40. }
  41. void Statemachine::DigitBuffer::addDigit(int d) {
  42. if (spaceLeft()) {
  43. digits[pos] = d;
  44. pos++;
  45. }
  46. }
  47. void Statemachine::DigitBuffer::removeDigit(void) {
  48. if (hasDigits()) {
  49. pos--;
  50. }
  51. }
  52. void Statemachine::DigitBuffer::clear(void) {
  53. pos = 0;
  54. }
  55. uint32_t Statemachine::DigitBuffer::getNumber(void) {
  56. uint32_t fact = 1;
  57. uint32_t sum = 0;
  58. for (int i = (pos - 1); i >= 0; i--) {
  59. sum += digits[i] * fact;
  60. fact *= 10;
  61. }
  62. return sum;
  63. }
  64. static const char *state_names[] = {
  65. stringify(init),
  66. stringify(menu),
  67. stringify(auto_mode),
  68. stringify(auto_fert),
  69. stringify(auto_fert_run),
  70. stringify(auto_tank_run),
  71. stringify(auto_plant),
  72. stringify(auto_plant_run),
  73. stringify(auto_done),
  74. stringify(menu_pumps),
  75. stringify(menu_pumps_time),
  76. stringify(menu_pumps_go),
  77. stringify(menu_pumps_run),
  78. stringify(menu_pumps_done),
  79. stringify(menu_valves),
  80. stringify(menu_valves_time),
  81. stringify(menu_valves_go),
  82. stringify(menu_valves_run),
  83. stringify(menu_valves_done),
  84. stringify(error)
  85. };
  86. const char *Statemachine::getStateName(void) {
  87. return state_names[state];
  88. }
  89. Statemachine::Statemachine(print_fn _print, backspace_fn _backspace)
  90. : db(7), selected_plants(plants.countPlants()) {
  91. state = init;
  92. old_state = init;
  93. print = _print;
  94. backspace = _backspace;
  95. selected_id = 0;
  96. selected_time = 0;
  97. start_time = 0;
  98. stop_time = 0;
  99. last_animation_time = 0;
  100. error_condition = "";
  101. }
  102. void Statemachine::begin(void) {
  103. switch_to(init);
  104. }
  105. void Statemachine::input(int n) {
  106. if (state == init) {
  107. switch_to(menu);
  108. } else if (state == menu) {
  109. if (n == 1) {
  110. switch_to(auto_mode);
  111. } else if (n == 2) {
  112. switch_to(menu_pumps);
  113. } else if (n == 3) {
  114. switch_to(menu_valves);
  115. } else if ((n == -1) || (n == -2)) {
  116. switch_to(init);
  117. }
  118. } else if (state == auto_mode) {
  119. if (n == 1) {
  120. switch_to(auto_fert);
  121. } else if (n == 2) {
  122. auto wl = plants.getWaterlevel();
  123. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  124. plants.openWaterInlet();
  125. selected_id = plants.countPlants() + 1;
  126. selected_time = MAX_TANK_FILL_TIME;
  127. start_time = millis();
  128. switch_to(auto_tank_run);
  129. } else if (wl == Plants::full) {
  130. stop_time = millis();
  131. switch_to(auto_mode);
  132. } else if (wl == Plants::invalid) {
  133. error_condition = "Invalid sensor state";
  134. state = auto_mode;
  135. switch_to(error);
  136. }
  137. } else if (n == 3) {
  138. selected_plants.clear();
  139. switch_to(auto_plant);
  140. } else if ((n == -1) || (n == -2)) {
  141. switch_to(menu);
  142. }
  143. } else if (state == auto_fert) {
  144. if ((n >= 1) && (n <= 3)) {
  145. auto wl = plants.getWaterlevel();
  146. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  147. plants.startFertilizer(n - 1);
  148. selected_id = n;
  149. selected_time = AUTO_PUMP_RUNTIME;
  150. start_time = millis();
  151. switch_to(auto_fert_run);
  152. } else if (wl == Plants::full) {
  153. stop_time = millis();
  154. switch_to(auto_mode);
  155. } else if (wl == Plants::invalid) {
  156. error_condition = "Invalid sensor state";
  157. state = auto_mode;
  158. switch_to(error);
  159. }
  160. } else if ((n == -1) || (n == -2)) {
  161. switch_to(auto_mode);
  162. }
  163. } else if (state == auto_fert_run) {
  164. plants.abort();
  165. stop_time = millis();
  166. switch_to(auto_done);
  167. } else if (state == auto_tank_run) {
  168. plants.abort();
  169. stop_time = millis();
  170. switch_to(auto_done);
  171. } else if (state == auto_plant) {
  172. if (n == -1) {
  173. if (db.hasDigits()) {
  174. backspace();
  175. db.removeDigit();
  176. } else {
  177. switch_to(auto_mode);
  178. }
  179. } else if (n == -2) {
  180. if (!db.hasDigits()) {
  181. auto wl = plants.getWaterlevel();
  182. if ((wl != Plants::empty) && (wl != Plants::invalid)) {
  183. for (int i = 0; i < plants.countPlants(); i++) {
  184. if (selected_plants.isSet(i)) {
  185. plants.startPlant(i);
  186. }
  187. }
  188. selected_time = MAX_AUTO_PLANT_RUNTIME;
  189. start_time = millis();
  190. switch_to(auto_plant_run);
  191. } else if (wl == Plants::empty) {
  192. stop_time = millis();
  193. switch_to(auto_mode);
  194. } else if (wl == Plants::invalid) {
  195. error_condition = "Invalid sensor state";
  196. state = auto_mode;
  197. switch_to(error);
  198. }
  199. } else {
  200. selected_id = number_input();
  201. if ((selected_id <= 0) || (selected_id > plants.countPlants())) {
  202. error_condition = "Invalid plant ID!";
  203. switch_to(error);
  204. } else {
  205. selected_plants.set(selected_id - 1);
  206. switch_to(auto_plant);
  207. }
  208. }
  209. } else {
  210. if (db.spaceLeft()) {
  211. db.addDigit(n);
  212. } else {
  213. backspace();
  214. }
  215. }
  216. } else if (state == auto_plant_run) {
  217. plants.abort();
  218. stop_time = millis();
  219. switch_to(auto_done);
  220. } else if (state == auto_done) {
  221. switch_to(auto_mode);
  222. } else if (state == menu_pumps) {
  223. if (n == -1) {
  224. if (db.hasDigits()) {
  225. backspace();
  226. db.removeDigit();
  227. } else {
  228. switch_to(menu);
  229. }
  230. } else if (n == -2) {
  231. if (!db.hasDigits()) {
  232. return;
  233. }
  234. selected_id = number_input();
  235. if ((selected_id <= 0) || (selected_id > plants.countFertilizers())) {
  236. error_condition = "Invalid pump ID!";
  237. switch_to(error);
  238. } else {
  239. switch_to(menu_pumps_time);
  240. }
  241. } else {
  242. if (db.spaceLeft()) {
  243. db.addDigit(n);
  244. } else {
  245. backspace();
  246. }
  247. }
  248. } else if (state == menu_pumps_time) {
  249. if (n == -1) {
  250. if (db.hasDigits()) {
  251. backspace();
  252. db.removeDigit();
  253. } else {
  254. switch_to(menu_pumps);
  255. }
  256. } else if (n == -2) {
  257. if (!db.hasDigits()) {
  258. return;
  259. }
  260. selected_time = number_input();
  261. if ((selected_time <= 0) || (selected_time > MAX_PUMP_RUNTIME)) {
  262. error_condition = "Invalid time range!";
  263. switch_to(error);
  264. } else {
  265. switch_to(menu_pumps_go);
  266. }
  267. } else {
  268. if (db.spaceLeft()) {
  269. db.addDigit(n);
  270. } else {
  271. backspace();
  272. }
  273. }
  274. } else if (state == menu_pumps_go) {
  275. if (n == -2) {
  276. start_time = millis();
  277. last_animation_time = start_time;
  278. auto wl = plants.getWaterlevel();
  279. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  280. plants.startFertilizer(selected_id - 1);
  281. switch_to(menu_pumps_run);
  282. } else if (wl == Plants::full) {
  283. stop_time = millis();
  284. switch_to(menu_pumps_done);
  285. } else if (wl == Plants::invalid) {
  286. error_condition = "Invalid sensor state";
  287. state = menu_pumps;
  288. switch_to(error);
  289. }
  290. } else {
  291. switch_to(menu_pumps_time);
  292. }
  293. } else if (state == menu_pumps_run) {
  294. plants.abort();
  295. stop_time = millis();
  296. switch_to(menu_pumps_done);
  297. } else if (state == menu_pumps_done) {
  298. switch_to(menu);
  299. } else if (state == menu_valves) {
  300. if (n == -1) {
  301. if (db.hasDigits()) {
  302. backspace();
  303. db.removeDigit();
  304. } else {
  305. switch_to(menu);
  306. }
  307. } else if (n == -2) {
  308. if (!db.hasDigits()) {
  309. return;
  310. }
  311. selected_id = number_input();
  312. if ((selected_id <= 0) || (selected_id > (plants.countPlants() + 1))) {
  313. error_condition = "Invalid valve ID!";
  314. switch_to(error);
  315. } else {
  316. switch_to(menu_valves_time);
  317. }
  318. } else {
  319. if (db.spaceLeft()) {
  320. db.addDigit(n);
  321. } else {
  322. backspace();
  323. }
  324. }
  325. } else if (state == menu_valves_time) {
  326. if (n == -1) {
  327. if (db.hasDigits()) {
  328. backspace();
  329. db.removeDigit();
  330. } else {
  331. switch_to(menu_valves);
  332. }
  333. } else if (n == -2) {
  334. if (!db.hasDigits()) {
  335. return;
  336. }
  337. selected_time = number_input();
  338. if ((selected_time <= 0) || (selected_time > MAX_VALVE_RUNTIME)) {
  339. error_condition = "Invalid time range!";
  340. switch_to(error);
  341. } else {
  342. switch_to(menu_valves_go);
  343. }
  344. } else {
  345. if (db.spaceLeft()) {
  346. db.addDigit(n);
  347. } else {
  348. backspace();
  349. }
  350. }
  351. } else if (state == menu_valves_go) {
  352. if (n == -2) {
  353. start_time = millis();
  354. last_animation_time = start_time;
  355. auto wl = plants.getWaterlevel();
  356. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  357. if (selected_id >= (plants.countPlants() + 1)) {
  358. plants.openWaterInlet();
  359. } else {
  360. plants.startPlant(selected_id - 1);
  361. }
  362. switch_to(menu_valves_run);
  363. } else if (wl == Plants::full) {
  364. stop_time = millis();
  365. switch_to(menu_valves_done);
  366. } else if (wl == Plants::invalid) {
  367. error_condition = "Invalid sensor state";
  368. state = menu_valves;
  369. switch_to(error);
  370. }
  371. } else {
  372. switch_to(menu_valves_time);
  373. }
  374. } else if (state == menu_valves_run) {
  375. plants.abort();
  376. stop_time = millis();
  377. switch_to(menu_valves_done);
  378. } else if (state == menu_valves_done) {
  379. switch_to(menu);
  380. } else if (state == error) {
  381. if (old_state != error) {
  382. switch_to(old_state);
  383. } else {
  384. switch_to(menu);
  385. }
  386. }
  387. }
  388. uint32_t Statemachine::number_input(void) {
  389. for (int i = 0; i < db.countDigits(); i++) {
  390. backspace();
  391. }
  392. uint32_t n = db.getNumber();
  393. db.clear();
  394. debug.print("Whole number input: ");
  395. debug.println(n);
  396. return n;
  397. }
  398. void Statemachine::act(void) {
  399. if ((state == menu_pumps_run) || (state == menu_valves_run)) {
  400. unsigned long runtime = millis() - start_time;
  401. if ((runtime / 1000UL) >= selected_time) {
  402. // stop if timeout has been reached
  403. plants.abort();
  404. stop_time = millis();
  405. switch_to((state == menu_pumps_run) ? menu_pumps_done : menu_valves_done);
  406. } else if ((millis() - last_animation_time) >= 500) {
  407. // update animation if needed
  408. last_animation_time = millis();
  409. switch_to(state);
  410. }
  411. }
  412. #ifdef CHECK_SENSORS_VALVE_PUMP_MENU_FULL
  413. if ((state == menu_pumps_run) || ((state == menu_valves_run) && (selected_id == (plants.countPlants() + 1)))) {
  414. // check water level state
  415. auto wl = plants.getWaterlevel();
  416. if (wl == Plants::full) {
  417. plants.abort();
  418. stop_time = millis();
  419. switch_to((state == menu_pumps_run) ? menu_pumps_done : menu_valves_done);
  420. } else if (wl == Plants::invalid) {
  421. plants.abort();
  422. error_condition = "Invalid sensor state";
  423. state = (state == menu_pumps_run) ? menu_pumps : menu_valves;
  424. switch_to(error);
  425. }
  426. }
  427. #endif // CHECK_SENSORS_VALVE_PUMP_MENU_FULL
  428. #ifdef CHECK_SENSORS_VALVE_PUMP_MENU_EMPTY
  429. if ((state == menu_valves_run) && (selected_id <= plants.countPlants())) {
  430. // check water level state
  431. auto wl = plants.getWaterlevel();
  432. if (wl == Plants::empty) {
  433. plants.abort();
  434. stop_time = millis();
  435. switch_to(menu_valves_done);
  436. } else if (wl == Plants::invalid) {
  437. plants.abort();
  438. error_condition = "Invalid sensor state";
  439. state = menu_valves;
  440. switch_to(error);
  441. }
  442. }
  443. #endif // CHECK_SENSORS_VALVE_PUMP_MENU_EMPTY
  444. if ((state == auto_fert_run) || (state == auto_tank_run)) {
  445. unsigned long runtime = millis() - start_time;
  446. if ((runtime / 1000UL) >= selected_time) {
  447. // stop if timeout has been reached
  448. plants.abort();
  449. stop_time = millis();
  450. switch_to(auto_done);
  451. } else if ((millis() - last_animation_time) >= 500) {
  452. // update animation if needed
  453. last_animation_time = millis();
  454. switch_to(state);
  455. }
  456. // check water level state
  457. auto wl = plants.getWaterlevel();
  458. if (wl == Plants::full) {
  459. plants.abort();
  460. stop_time = millis();
  461. switch_to(auto_done);
  462. } else if (wl == Plants::invalid) {
  463. plants.abort();
  464. error_condition = "Invalid sensor state";
  465. state = auto_mode;
  466. switch_to(error);
  467. }
  468. }
  469. if (state == auto_plant_run) {
  470. unsigned long runtime = millis() - start_time;
  471. if ((runtime / 1000UL) >= selected_time) {
  472. // stop if timeout has been reached
  473. plants.abort();
  474. stop_time = millis();
  475. switch_to(auto_done);
  476. } else if ((millis() - last_animation_time) >= 500) {
  477. // update animation if needed
  478. last_animation_time = millis();
  479. switch_to(state);
  480. }
  481. // check water level state
  482. auto wl = plants.getWaterlevel();
  483. if (wl == Plants::empty) {
  484. plants.abort();
  485. stop_time = millis();
  486. switch_to(auto_done);
  487. } else if (wl == Plants::invalid) {
  488. plants.abort();
  489. error_condition = "Invalid sensor state";
  490. state = auto_mode;
  491. switch_to(error);
  492. }
  493. }
  494. }
  495. void Statemachine::switch_to(States s) {
  496. old_state = state;
  497. state = s;
  498. if (s == init) {
  499. String a = String("- Giess-o-mat V") + FIRMWARE_VERSION + String(" -");
  500. print(a.c_str(),
  501. "Usage: Enter number",
  502. "* Delete prev. digit",
  503. "# Execute input num.",
  504. -1);
  505. } else if (s == menu) {
  506. print("------- Menu -------",
  507. "1: Automatic program",
  508. "2: Fertilizer pumps",
  509. "3: Outlet valves",
  510. -1);
  511. } else if (s == auto_mode) {
  512. print("------- Auto -------",
  513. "1: Add Fertilizer",
  514. "2: Fill Reservoir",
  515. "3: Water a plant",
  516. -1);
  517. } else if (s == auto_fert) {
  518. print("---- Fertilizer ----",
  519. "1: Vegetation Phase",
  520. "2: Bloom Phase",
  521. "3: Special",
  522. -1);
  523. } else if (s == auto_fert_run) {
  524. unsigned long runtime = millis() - start_time;
  525. String a = String("Time: ") + String(runtime / 1000UL) + String("s / ") + String(selected_time) + String('s');
  526. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  527. String b;
  528. for (unsigned long i = 0; i < anim; i++) {
  529. b += '#';
  530. }
  531. print("---- Dispensing ----",
  532. a.c_str(),
  533. b.c_str(),
  534. "Hit any key to stop!",
  535. -1);
  536. } else if (s == auto_tank_run) {
  537. unsigned long runtime = millis() - start_time;
  538. String a = String("Time: ") + String(runtime / 1000UL) + String("s / ") + String(selected_time) + String('s');
  539. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  540. String b;
  541. for (unsigned long i = 0; i < anim; i++) {
  542. b += '#';
  543. }
  544. print("--- Filling Tank ---",
  545. a.c_str(),
  546. b.c_str(),
  547. "Hit any key to stop!",
  548. -1);
  549. } else if (s == auto_plant) {
  550. String a = String("(Input 1 to ") + String(plants.countPlants()) + String(")");
  551. print("--- Select Plant ---",
  552. "Leave empty if done!",
  553. a.c_str(),
  554. "Plant: ",
  555. 3);
  556. } else if (s == auto_plant_run) {
  557. unsigned long runtime = millis() - start_time;
  558. String a = String("Time: ") + String(runtime / 1000UL) + String("s / ") + String(selected_time) + String('s');
  559. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  560. String b;
  561. for (unsigned long i = 0; i < anim; i++) {
  562. b += '#';
  563. }
  564. print("----- Watering -----",
  565. a.c_str(),
  566. b.c_str(),
  567. "Hit any key to stop!",
  568. -1);
  569. } else if (s == auto_done) {
  570. String a = String("after ") + String((stop_time - start_time) / 1000UL) + String("s.");
  571. print("------- Done -------",
  572. "Dispensing finished",
  573. a.c_str(),
  574. "Hit any key for menu",
  575. -1);
  576. #if defined(PLATFORM_ESP)
  577. unsigned long runtime = stop_time - start_time;
  578. if (old_state == auto_plant_run) {
  579. for (int i = 0; i < plants.countPlants(); i++) {
  580. if (selected_plants.isSet(i)) {
  581. bool success = wifi_write_database(runtime / 1000, "plant", i + 1);
  582. if (!success) {
  583. debug.print("Error writing to InfluxDB ");
  584. debug.print(INFLUXDB_HOST);
  585. debug.print(":");
  586. debug.print(INFLUXDB_PORT);
  587. debug.print("/");
  588. debug.print(INFLUXDB_DATABASE);
  589. debug.println("/plant");
  590. }
  591. }
  592. }
  593. } else if (old_state == auto_fert_run) {
  594. bool success = wifi_write_database(runtime / 1000, "fertilizer", selected_id);
  595. if (!success) {
  596. debug.print("Error writing to InfluxDB ");
  597. debug.print(INFLUXDB_HOST);
  598. debug.print(":");
  599. debug.print(INFLUXDB_PORT);
  600. debug.print("/");
  601. debug.print(INFLUXDB_DATABASE);
  602. debug.println("/fertilizer");
  603. }
  604. }
  605. #endif // PLATFORM_ESP
  606. } else if (s == menu_pumps) {
  607. String a = String("(Input 1 to ") + String(plants.countFertilizers()) + String(")");
  608. print("------- Pump -------",
  609. "Please select pump",
  610. a.c_str(),
  611. "Pump: ",
  612. 3);
  613. } else if (s == menu_pumps_time) {
  614. String header = String("------ Pump ") + String(selected_id) + String(" ------");
  615. print(header.c_str(),
  616. "Please set runtime",
  617. "(Input in seconds)",
  618. "Runtime: ",
  619. 3);
  620. } else if (s == menu_pumps_go) {
  621. String a = String("Pump No. ") + String(selected_id);
  622. String b = String("Runtime ") + String(selected_time) + String('s');
  623. print("----- Confirm? -----",
  624. a.c_str(),
  625. b.c_str(),
  626. " # Confirm",
  627. -1);
  628. } else if (s == menu_pumps_run) {
  629. unsigned long runtime = millis() - start_time;
  630. String a = String("Time: ") + String(runtime / 1000UL) + String("s / ") + String(selected_time) + String('s');
  631. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  632. String b;
  633. for (unsigned long i = 0; i < anim; i++) {
  634. b += '#';
  635. }
  636. print("---- Dispensing ----",
  637. a.c_str(),
  638. b.c_str(),
  639. "Hit any key to stop!",
  640. -1);
  641. } else if (s == menu_pumps_done) {
  642. String a = String("after ") + String((stop_time - start_time) / 1000UL) + String("s.");
  643. print("------- Done -------",
  644. "Dispensing finished",
  645. a.c_str(),
  646. "Hit any key for menu",
  647. -1);
  648. #if defined(PLATFORM_ESP)
  649. unsigned long runtime = stop_time - start_time;
  650. bool success = wifi_write_database(runtime / 1000, "fertilizer", selected_id);
  651. if (!success) {
  652. debug.print("Error writing to InfluxDB ");
  653. debug.print(INFLUXDB_HOST);
  654. debug.print(":");
  655. debug.print(INFLUXDB_PORT);
  656. debug.print("/");
  657. debug.print(INFLUXDB_DATABASE);
  658. debug.println("/fertilizer");
  659. }
  660. #endif // PLATFORM_ESP
  661. } else if (s == menu_valves) {
  662. String a = String("(Input 1 to ") + String(plants.countPlants() + 1) + String(")");
  663. print("------ Valves ------",
  664. "Please select valve",
  665. a.c_str(),
  666. "Valve: ",
  667. 3);
  668. } else if (s == menu_valves_time) {
  669. String header = String("----- Valve ") + String(selected_id) + String(" -----");
  670. print(header.c_str(),
  671. "Please set runtime",
  672. "(Input in seconds)",
  673. "Runtime: ",
  674. 3);
  675. } else if (s == menu_valves_go) {
  676. String a = String("Valve No. ") + String(selected_id);
  677. String b = String("Runtime ") + String(selected_time) + String('s');
  678. print("----- Confirm? -----",
  679. a.c_str(),
  680. b.c_str(),
  681. " # Confirm",
  682. -1);
  683. } else if (s == menu_valves_run) {
  684. unsigned long runtime = millis() - start_time;
  685. String a = String("Time: ") + String(runtime / 1000UL) + String("s / ") + String(selected_time) + String('s');
  686. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  687. String b;
  688. for (unsigned long i = 0; i <= anim; i++) {
  689. b += '#';
  690. }
  691. print("---- Dispensing ----",
  692. a.c_str(),
  693. b.c_str(),
  694. "Hit any key to stop!",
  695. -1);
  696. } else if (s == menu_valves_done) {
  697. String a = String("after ") + String((stop_time - start_time) / 1000UL) + String("s.");
  698. print("------- Done -------",
  699. "Dispensing finished",
  700. a.c_str(),
  701. "Hit any key for menu",
  702. -1);
  703. #if defined(PLATFORM_ESP)
  704. unsigned long runtime = stop_time - start_time;
  705. if (selected_id <= plants.countPlants()) {
  706. bool success = wifi_write_database(runtime / 1000, "plant", selected_id);
  707. if (!success) {
  708. debug.print("Error writing to InfluxDB ");
  709. debug.print(INFLUXDB_HOST);
  710. debug.print(":");
  711. debug.print(INFLUXDB_PORT);
  712. debug.print("/");
  713. debug.print(INFLUXDB_DATABASE);
  714. debug.println("/plant");
  715. }
  716. }
  717. #endif // PLATFORM_ESP
  718. } else if (s == error) {
  719. print("------ Error! ------",
  720. "There is a problem:",
  721. error_condition.c_str(),
  722. " Press any key...",
  723. -1);
  724. }
  725. }