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 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925
  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_a),
  68. stringify(auto_mode_b),
  69. stringify(auto_fert),
  70. stringify(auto_fert_run),
  71. stringify(auto_tank_run),
  72. stringify(auto_plant),
  73. stringify(auto_plant_run),
  74. stringify(auto_done),
  75. stringify(fillnwater_plant),
  76. stringify(fillnwater_tank_run),
  77. stringify(fillnwater_plant_run),
  78. stringify(menu_pumps),
  79. stringify(menu_pumps_time),
  80. stringify(menu_pumps_go),
  81. stringify(menu_pumps_run),
  82. stringify(menu_pumps_done),
  83. stringify(menu_valves),
  84. stringify(menu_valves_time),
  85. stringify(menu_valves_go),
  86. stringify(menu_valves_run),
  87. stringify(menu_valves_done),
  88. stringify(error)
  89. };
  90. const char *Statemachine::getStateName(void) {
  91. return state_names[state];
  92. }
  93. Statemachine::Statemachine(print_fn _print, backspace_fn _backspace)
  94. : db(7), selected_plants(plants.countPlants()) {
  95. state = init;
  96. old_state = init;
  97. print = _print;
  98. backspace = _backspace;
  99. selected_id = 0;
  100. selected_time = 0;
  101. start_time = 0;
  102. stop_time = 0;
  103. last_animation_time = 0;
  104. error_condition = "";
  105. }
  106. void Statemachine::begin(void) {
  107. switch_to(init);
  108. }
  109. void Statemachine::input(int n) {
  110. if (state == init) {
  111. switch_to(menu);
  112. } else if (state == menu) {
  113. if (n == 1) {
  114. switch_to(auto_mode_a);
  115. } else if (n == 2) {
  116. switch_to(menu_pumps);
  117. } else if (n == 3) {
  118. switch_to(menu_valves);
  119. } else if ((n == -1) || (n == -2)) {
  120. switch_to(init);
  121. }
  122. } else if ((state == auto_mode_a) || (state == auto_mode_b)) {
  123. if (n == 1) {
  124. switch_to(auto_fert);
  125. } else if (n == 2) {
  126. selected_plants.clear();
  127. switch_to(fillnwater_plant);
  128. } else if (n == 3) {
  129. auto wl = plants.getWaterlevel();
  130. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  131. plants.openWaterInlet();
  132. selected_id = plants.countPlants() + 1;
  133. selected_time = MAX_TANK_FILL_TIME;
  134. start_time = millis();
  135. switch_to(auto_tank_run);
  136. } else if (wl == Plants::full) {
  137. stop_time = millis();
  138. switch_to(auto_mode_a);
  139. } else if (wl == Plants::invalid) {
  140. error_condition = "Invalid sensor state";
  141. state = auto_mode_a;
  142. switch_to(error);
  143. }
  144. } else if (n == 4) {
  145. selected_plants.clear();
  146. switch_to(auto_plant);
  147. } else if (n == -1) {
  148. switch_to(menu);
  149. } else if (n == -2) {
  150. switch_to((state == auto_mode_a) ? auto_mode_b : auto_mode_a);
  151. }
  152. } else if (state == auto_fert) {
  153. if ((n >= 1) && (n <= 3)) {
  154. auto wl = plants.getWaterlevel();
  155. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  156. plants.startFertilizer(n - 1);
  157. selected_id = n;
  158. selected_time = AUTO_PUMP_RUNTIME;
  159. start_time = millis();
  160. switch_to(auto_fert_run);
  161. } else if (wl == Plants::full) {
  162. stop_time = millis();
  163. switch_to(auto_mode_a);
  164. } else if (wl == Plants::invalid) {
  165. error_condition = "Invalid sensor state";
  166. state = auto_mode_a;
  167. switch_to(error);
  168. }
  169. } else if ((n == -1) || (n == -2)) {
  170. switch_to(auto_mode_a);
  171. }
  172. } else if (state == auto_fert_run) {
  173. plants.abort();
  174. stop_time = millis();
  175. switch_to(auto_done);
  176. } else if (state == auto_tank_run) {
  177. plants.abort();
  178. stop_time = millis();
  179. switch_to(auto_done);
  180. } else if (state == auto_plant) {
  181. if (n == -1) {
  182. if (db.hasDigits()) {
  183. backspace();
  184. db.removeDigit();
  185. } else {
  186. switch_to(auto_mode_b);
  187. }
  188. } else if (n == -2) {
  189. if (!db.hasDigits()) {
  190. auto wl = plants.getWaterlevel();
  191. if ((wl != Plants::empty) && (wl != Plants::invalid)) {
  192. for (int i = 0; i < plants.countPlants(); i++) {
  193. if (selected_plants.isSet(i)) {
  194. plants.startPlant(i);
  195. }
  196. }
  197. selected_time = MAX_AUTO_PLANT_RUNTIME;
  198. start_time = millis();
  199. switch_to(auto_plant_run);
  200. } else if (wl == Plants::empty) {
  201. stop_time = millis();
  202. switch_to(auto_mode_b);
  203. } else if (wl == Plants::invalid) {
  204. error_condition = "Invalid sensor state";
  205. state = auto_mode_b;
  206. switch_to(error);
  207. }
  208. } else {
  209. selected_id = number_input();
  210. if ((selected_id <= 0) || (selected_id > plants.countPlants())) {
  211. error_condition = "Invalid plant ID!";
  212. switch_to(error);
  213. } else {
  214. selected_plants.set(selected_id - 1);
  215. switch_to(auto_plant);
  216. }
  217. }
  218. } else {
  219. if (db.spaceLeft()) {
  220. db.addDigit(n);
  221. } else {
  222. backspace();
  223. }
  224. }
  225. } else if (state == auto_plant_run) {
  226. plants.abort();
  227. stop_time = millis();
  228. switch_to(auto_done);
  229. } else if (state == auto_done) {
  230. switch_to(auto_mode_a);
  231. } else if (state == menu_pumps) {
  232. if (n == -1) {
  233. if (db.hasDigits()) {
  234. backspace();
  235. db.removeDigit();
  236. } else {
  237. switch_to(menu);
  238. }
  239. } else if (n == -2) {
  240. if (!db.hasDigits()) {
  241. return;
  242. }
  243. selected_id = number_input();
  244. if ((selected_id <= 0) || (selected_id > plants.countFertilizers())) {
  245. error_condition = "Invalid pump ID!";
  246. switch_to(error);
  247. } else {
  248. switch_to(menu_pumps_time);
  249. }
  250. } else {
  251. if (db.spaceLeft()) {
  252. db.addDigit(n);
  253. } else {
  254. backspace();
  255. }
  256. }
  257. } else if (state == fillnwater_plant) {
  258. if (n == -1) {
  259. if (db.hasDigits()) {
  260. backspace();
  261. db.removeDigit();
  262. } else {
  263. switch_to(auto_mode_a);
  264. }
  265. } else if (n == -2) {
  266. if (!db.hasDigits()) {
  267. int found = 0;
  268. for (int i = 0; i < plants.countPlants(); i++) {
  269. if (selected_plants.isSet(i)) {
  270. found = 1;
  271. break;
  272. }
  273. }
  274. if (found != 0) {
  275. auto wl = plants.getWaterlevel();
  276. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  277. plants.openWaterInlet();
  278. selected_id = plants.countPlants() + 1;
  279. selected_time = MAX_TANK_FILL_TIME;
  280. start_time = millis();
  281. switch_to(fillnwater_tank_run);
  282. } else if (wl == Plants::full) {
  283. stop_time = millis();
  284. auto wl = plants.getWaterlevel();
  285. if ((wl != Plants::empty) && (wl != Plants::invalid)) {
  286. for (int i = 0; i < plants.countPlants(); i++) {
  287. if (selected_plants.isSet(i)) {
  288. plants.startPlant(i);
  289. }
  290. }
  291. selected_time = MAX_AUTO_PLANT_RUNTIME;
  292. start_time = millis();
  293. switch_to(fillnwater_plant_run);
  294. } else if (wl == Plants::empty) {
  295. stop_time = millis();
  296. switch_to(auto_mode_a);
  297. } else if (wl == Plants::invalid) {
  298. error_condition = "Invalid sensor state";
  299. state = auto_mode_a;
  300. switch_to(error);
  301. }
  302. } else if (wl == Plants::invalid) {
  303. error_condition = "Invalid sensor state";
  304. state = auto_mode_a;
  305. switch_to(error);
  306. }
  307. }
  308. } else {
  309. selected_id = number_input();
  310. if ((selected_id <= 0) || (selected_id > plants.countPlants())) {
  311. error_condition = "Invalid plant ID!";
  312. switch_to(error);
  313. } else {
  314. selected_plants.set(selected_id - 1);
  315. switch_to(fillnwater_plant);
  316. }
  317. }
  318. } else {
  319. if (db.spaceLeft()) {
  320. db.addDigit(n);
  321. } else {
  322. backspace();
  323. }
  324. }
  325. } else if (state == fillnwater_tank_run) {
  326. plants.abort();
  327. stop_time = millis();
  328. auto wl = plants.getWaterlevel();
  329. if ((wl != Plants::empty) && (wl != Plants::invalid)) {
  330. for (int i = 0; i < plants.countPlants(); i++) {
  331. if (selected_plants.isSet(i)) {
  332. plants.startPlant(i);
  333. }
  334. }
  335. selected_time = MAX_AUTO_PLANT_RUNTIME;
  336. start_time = millis();
  337. switch_to(fillnwater_plant_run);
  338. } else if (wl == Plants::empty) {
  339. stop_time = millis();
  340. switch_to(auto_mode_a);
  341. } else if (wl == Plants::invalid) {
  342. error_condition = "Invalid sensor state";
  343. state = auto_mode_a;
  344. switch_to(error);
  345. }
  346. } else if (state == fillnwater_plant_run) {
  347. plants.abort();
  348. stop_time = millis();
  349. switch_to(auto_done);
  350. } else if (state == menu_pumps_time) {
  351. if (n == -1) {
  352. if (db.hasDigits()) {
  353. backspace();
  354. db.removeDigit();
  355. } else {
  356. switch_to(menu_pumps);
  357. }
  358. } else if (n == -2) {
  359. if (!db.hasDigits()) {
  360. return;
  361. }
  362. selected_time = number_input();
  363. if ((selected_time <= 0) || (selected_time > MAX_PUMP_RUNTIME)) {
  364. error_condition = "Invalid time range!";
  365. switch_to(error);
  366. } else {
  367. switch_to(menu_pumps_go);
  368. }
  369. } else {
  370. if (db.spaceLeft()) {
  371. db.addDigit(n);
  372. } else {
  373. backspace();
  374. }
  375. }
  376. } else if (state == menu_pumps_go) {
  377. if (n == -2) {
  378. start_time = millis();
  379. last_animation_time = start_time;
  380. auto wl = plants.getWaterlevel();
  381. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  382. plants.startFertilizer(selected_id - 1);
  383. switch_to(menu_pumps_run);
  384. } else if (wl == Plants::full) {
  385. stop_time = millis();
  386. switch_to(menu_pumps_done);
  387. } else if (wl == Plants::invalid) {
  388. error_condition = "Invalid sensor state";
  389. state = menu_pumps;
  390. switch_to(error);
  391. }
  392. } else {
  393. switch_to(menu_pumps_time);
  394. }
  395. } else if (state == menu_pumps_run) {
  396. plants.abort();
  397. stop_time = millis();
  398. switch_to(menu_pumps_done);
  399. } else if (state == menu_pumps_done) {
  400. switch_to(menu);
  401. } else if (state == menu_valves) {
  402. if (n == -1) {
  403. if (db.hasDigits()) {
  404. backspace();
  405. db.removeDigit();
  406. } else {
  407. switch_to(menu);
  408. }
  409. } else if (n == -2) {
  410. if (!db.hasDigits()) {
  411. return;
  412. }
  413. selected_id = number_input();
  414. if ((selected_id <= 0) || (selected_id > (plants.countPlants() + 1))) {
  415. error_condition = "Invalid valve ID!";
  416. switch_to(error);
  417. } else {
  418. switch_to(menu_valves_time);
  419. }
  420. } else {
  421. if (db.spaceLeft()) {
  422. db.addDigit(n);
  423. } else {
  424. backspace();
  425. }
  426. }
  427. } else if (state == menu_valves_time) {
  428. if (n == -1) {
  429. if (db.hasDigits()) {
  430. backspace();
  431. db.removeDigit();
  432. } else {
  433. switch_to(menu_valves);
  434. }
  435. } else if (n == -2) {
  436. if (!db.hasDigits()) {
  437. return;
  438. }
  439. selected_time = number_input();
  440. if ((selected_time <= 0) || (selected_time > MAX_VALVE_RUNTIME)) {
  441. error_condition = "Invalid time range!";
  442. switch_to(error);
  443. } else {
  444. switch_to(menu_valves_go);
  445. }
  446. } else {
  447. if (db.spaceLeft()) {
  448. db.addDigit(n);
  449. } else {
  450. backspace();
  451. }
  452. }
  453. } else if (state == menu_valves_go) {
  454. if (n == -2) {
  455. start_time = millis();
  456. last_animation_time = start_time;
  457. auto wl = plants.getWaterlevel();
  458. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  459. if (selected_id >= (plants.countPlants() + 1)) {
  460. plants.openWaterInlet();
  461. } else {
  462. plants.startPlant(selected_id - 1);
  463. }
  464. switch_to(menu_valves_run);
  465. } else if (wl == Plants::full) {
  466. stop_time = millis();
  467. switch_to(menu_valves_done);
  468. } else if (wl == Plants::invalid) {
  469. error_condition = "Invalid sensor state";
  470. state = menu_valves;
  471. switch_to(error);
  472. }
  473. } else {
  474. switch_to(menu_valves_time);
  475. }
  476. } else if (state == menu_valves_run) {
  477. plants.abort();
  478. stop_time = millis();
  479. switch_to(menu_valves_done);
  480. } else if (state == menu_valves_done) {
  481. switch_to(menu);
  482. } else if (state == error) {
  483. if (old_state != error) {
  484. switch_to(old_state);
  485. } else {
  486. switch_to(menu);
  487. }
  488. }
  489. }
  490. uint32_t Statemachine::number_input(void) {
  491. for (int i = 0; i < db.countDigits(); i++) {
  492. backspace();
  493. }
  494. uint32_t n = db.getNumber();
  495. db.clear();
  496. debug.print("Whole number input: ");
  497. debug.println(n);
  498. return n;
  499. }
  500. void Statemachine::act(void) {
  501. if ((state == menu_pumps_run) || (state == menu_valves_run)) {
  502. unsigned long runtime = millis() - start_time;
  503. if ((runtime / 1000UL) >= selected_time) {
  504. // stop if timeout has been reached
  505. plants.abort();
  506. stop_time = millis();
  507. switch_to((state == menu_pumps_run) ? menu_pumps_done : menu_valves_done);
  508. } else if ((millis() - last_animation_time) >= 500) {
  509. // update animation if needed
  510. last_animation_time = millis();
  511. switch_to(state);
  512. }
  513. }
  514. #ifdef CHECK_SENSORS_VALVE_PUMP_MENU_FULL
  515. if ((state == menu_pumps_run) || ((state == menu_valves_run) && (selected_id == (plants.countPlants() + 1)))) {
  516. // check water level state
  517. auto wl = plants.getWaterlevel();
  518. if (wl == Plants::full) {
  519. plants.abort();
  520. stop_time = millis();
  521. switch_to((state == menu_pumps_run) ? menu_pumps_done : menu_valves_done);
  522. } else if (wl == Plants::invalid) {
  523. plants.abort();
  524. error_condition = "Invalid sensor state";
  525. state = (state == menu_pumps_run) ? menu_pumps : menu_valves;
  526. switch_to(error);
  527. }
  528. }
  529. #endif // CHECK_SENSORS_VALVE_PUMP_MENU_FULL
  530. #ifdef CHECK_SENSORS_VALVE_PUMP_MENU_EMPTY
  531. if ((state == menu_valves_run) && (selected_id <= plants.countPlants())) {
  532. // check water level state
  533. auto wl = plants.getWaterlevel();
  534. if (wl == Plants::empty) {
  535. plants.abort();
  536. stop_time = millis();
  537. switch_to(menu_valves_done);
  538. } else if (wl == Plants::invalid) {
  539. plants.abort();
  540. error_condition = "Invalid sensor state";
  541. state = menu_valves;
  542. switch_to(error);
  543. }
  544. }
  545. #endif // CHECK_SENSORS_VALVE_PUMP_MENU_EMPTY
  546. if ((state == auto_fert_run) || (state == auto_tank_run) || (state == fillnwater_tank_run)) {
  547. unsigned long runtime = millis() - start_time;
  548. if ((runtime / 1000UL) >= selected_time) {
  549. // stop if timeout has been reached
  550. plants.abort();
  551. stop_time = millis();
  552. switch_to(auto_done);
  553. } else if ((millis() - last_animation_time) >= 500) {
  554. // update animation if needed
  555. last_animation_time = millis();
  556. switch_to(state);
  557. }
  558. // check water level state
  559. auto wl = plants.getWaterlevel();
  560. if (wl == Plants::full) {
  561. plants.abort();
  562. stop_time = millis();
  563. if (state == fillnwater_tank_run) {
  564. auto wl = plants.getWaterlevel();
  565. if ((wl != Plants::empty) && (wl != Plants::invalid)) {
  566. for (int i = 0; i < plants.countPlants(); i++) {
  567. if (selected_plants.isSet(i)) {
  568. plants.startPlant(i);
  569. }
  570. }
  571. selected_time = MAX_AUTO_PLANT_RUNTIME;
  572. start_time = millis();
  573. switch_to(fillnwater_plant_run);
  574. } else if (wl == Plants::empty) {
  575. stop_time = millis();
  576. switch_to(auto_mode_a);
  577. } else if (wl == Plants::invalid) {
  578. error_condition = "Invalid sensor state";
  579. state = auto_mode_a;
  580. switch_to(error);
  581. }
  582. } else {
  583. switch_to(auto_done);
  584. }
  585. } else if (wl == Plants::invalid) {
  586. plants.abort();
  587. error_condition = "Invalid sensor state";
  588. state = auto_mode_a;
  589. switch_to(error);
  590. }
  591. }
  592. if ((state == auto_plant_run) || (state == fillnwater_plant_run)) {
  593. unsigned long runtime = millis() - start_time;
  594. if ((runtime / 1000UL) >= selected_time) {
  595. // stop if timeout has been reached
  596. plants.abort();
  597. stop_time = millis();
  598. switch_to(auto_done);
  599. } else if ((millis() - last_animation_time) >= 500) {
  600. // update animation if needed
  601. last_animation_time = millis();
  602. switch_to(state);
  603. }
  604. // check water level state
  605. auto wl = plants.getWaterlevel();
  606. if (wl == Plants::empty) {
  607. plants.abort();
  608. stop_time = millis();
  609. switch_to(auto_done);
  610. } else if (wl == Plants::invalid) {
  611. plants.abort();
  612. error_condition = "Invalid sensor state";
  613. state = auto_mode_a;
  614. switch_to(error);
  615. }
  616. }
  617. }
  618. void Statemachine::switch_to(States s) {
  619. old_state = state;
  620. state = s;
  621. if (s == init) {
  622. String a = String("- Giess-o-mat V") + FIRMWARE_VERSION + String(" -");
  623. print(a.c_str(),
  624. "Usage: Enter number",
  625. "* Delete prev. digit",
  626. "# Execute input num.",
  627. -1);
  628. } else if (s == menu) {
  629. print("------- Menu -------",
  630. "1: Automatic program",
  631. "2: Fertilizer pumps",
  632. "3: Outlet valves",
  633. -1);
  634. } else if (s == auto_mode_a) {
  635. print("----- Auto 1/2 -----",
  636. "1: Add Fertilizer",
  637. "2: Fill 'n' Water",
  638. "#: Go to page 2/2...",
  639. -1);
  640. } else if (s == auto_mode_b) {
  641. print("----- Auto 2/2 -----",
  642. "3: Fill Reservoir",
  643. "4: Water a plant",
  644. "#: Go to page 1/2...",
  645. -1);
  646. } else if (s == auto_fert) {
  647. print("---- Fertilizer ----",
  648. "1: Vegetation Phase",
  649. "2: Bloom Phase",
  650. "3: Special",
  651. -1);
  652. } else if (s == auto_fert_run) {
  653. unsigned long runtime = millis() - start_time;
  654. String a = String("Time: ") + String(runtime / 1000UL) + String("s / ") + String(selected_time) + String('s');
  655. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  656. String b;
  657. for (unsigned long i = 0; i < anim; i++) {
  658. b += '#';
  659. }
  660. print("---- Dispensing ----",
  661. a.c_str(),
  662. b.c_str(),
  663. "Hit any key to stop!",
  664. -1);
  665. } else if ((s == auto_tank_run) || (s == fillnwater_tank_run)) {
  666. unsigned long runtime = millis() - start_time;
  667. String a = String("Time: ") + String(runtime / 1000UL) + String("s / ") + String(selected_time) + String('s');
  668. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  669. String b;
  670. for (unsigned long i = 0; i < anim; i++) {
  671. b += '#';
  672. }
  673. print("--- Filling Tank ---",
  674. a.c_str(),
  675. b.c_str(),
  676. "Hit any key to stop!",
  677. -1);
  678. } else if ((s == auto_plant) || (s == fillnwater_plant)) {
  679. String a = String("(Input 1 to ") + String(plants.countPlants()) + String(")");
  680. print("--- Select Plant ---",
  681. "Leave empty if done!",
  682. a.c_str(),
  683. "Plant: ",
  684. 3);
  685. } else if ((s == auto_plant_run) || (s == fillnwater_plant_run)) {
  686. unsigned long runtime = millis() - start_time;
  687. String a = String("Time: ") + String(runtime / 1000UL) + String("s / ") + String(selected_time) + String('s');
  688. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  689. String b;
  690. for (unsigned long i = 0; i < anim; i++) {
  691. b += '#';
  692. }
  693. print("----- Watering -----",
  694. a.c_str(),
  695. b.c_str(),
  696. "Hit any key to stop!",
  697. -1);
  698. } else if (s == auto_done) {
  699. String a = String("after ") + String((stop_time - start_time) / 1000UL) + String("s.");
  700. print("------- Done -------",
  701. "Dispensing finished",
  702. a.c_str(),
  703. "Hit any key for menu",
  704. -1);
  705. #if defined(PLATFORM_ESP)
  706. unsigned long runtime = stop_time - start_time;
  707. if ((old_state == auto_plant_run) || (old_state == fillnwater_plant_run)) {
  708. for (int i = 0; i < plants.countPlants(); i++) {
  709. if (selected_plants.isSet(i)) {
  710. bool success = wifi_write_database(runtime / 1000, "plant", i + 1);
  711. if (!success) {
  712. debug.print("Error writing to InfluxDB ");
  713. debug.print(INFLUXDB_HOST);
  714. debug.print(":");
  715. debug.print(INFLUXDB_PORT);
  716. debug.print("/");
  717. debug.print(INFLUXDB_DATABASE);
  718. debug.println("/plant");
  719. }
  720. }
  721. }
  722. } else if (old_state == auto_fert_run) {
  723. bool success = wifi_write_database(runtime / 1000, "fertilizer", selected_id);
  724. if (!success) {
  725. debug.print("Error writing to InfluxDB ");
  726. debug.print(INFLUXDB_HOST);
  727. debug.print(":");
  728. debug.print(INFLUXDB_PORT);
  729. debug.print("/");
  730. debug.print(INFLUXDB_DATABASE);
  731. debug.println("/fertilizer");
  732. }
  733. }
  734. #endif // PLATFORM_ESP
  735. } else if (s == menu_pumps) {
  736. String a = String("(Input 1 to ") + String(plants.countFertilizers()) + String(")");
  737. print("------- Pump -------",
  738. "Please select pump",
  739. a.c_str(),
  740. "Pump: ",
  741. 3);
  742. } else if (s == menu_pumps_time) {
  743. String header = String("------ Pump ") + String(selected_id) + String(" ------");
  744. print(header.c_str(),
  745. "Please set runtime",
  746. "(Input in seconds)",
  747. "Runtime: ",
  748. 3);
  749. } else if (s == menu_pumps_go) {
  750. String a = String("Pump No. ") + String(selected_id);
  751. String b = String("Runtime ") + String(selected_time) + String('s');
  752. print("----- Confirm? -----",
  753. a.c_str(),
  754. b.c_str(),
  755. " # Confirm",
  756. -1);
  757. } else if (s == menu_pumps_run) {
  758. unsigned long runtime = millis() - start_time;
  759. String a = String("Time: ") + String(runtime / 1000UL) + String("s / ") + String(selected_time) + String('s');
  760. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  761. String b;
  762. for (unsigned long i = 0; i < anim; i++) {
  763. b += '#';
  764. }
  765. print("---- Dispensing ----",
  766. a.c_str(),
  767. b.c_str(),
  768. "Hit any key to stop!",
  769. -1);
  770. } else if (s == menu_pumps_done) {
  771. String a = String("after ") + String((stop_time - start_time) / 1000UL) + String("s.");
  772. print("------- Done -------",
  773. "Dispensing finished",
  774. a.c_str(),
  775. "Hit any key for menu",
  776. -1);
  777. #if defined(PLATFORM_ESP)
  778. unsigned long runtime = stop_time - start_time;
  779. bool success = wifi_write_database(runtime / 1000, "fertilizer", selected_id);
  780. if (!success) {
  781. debug.print("Error writing to InfluxDB ");
  782. debug.print(INFLUXDB_HOST);
  783. debug.print(":");
  784. debug.print(INFLUXDB_PORT);
  785. debug.print("/");
  786. debug.print(INFLUXDB_DATABASE);
  787. debug.println("/fertilizer");
  788. }
  789. #endif // PLATFORM_ESP
  790. } else if (s == menu_valves) {
  791. String a = String("(Input 1 to ") + String(plants.countPlants() + 1) + String(")");
  792. print("------ Valves ------",
  793. "Please select valve",
  794. a.c_str(),
  795. "Valve: ",
  796. 3);
  797. } else if (s == menu_valves_time) {
  798. String header = String("----- Valve ") + String(selected_id) + String(" -----");
  799. print(header.c_str(),
  800. "Please set runtime",
  801. "(Input in seconds)",
  802. "Runtime: ",
  803. 3);
  804. } else if (s == menu_valves_go) {
  805. String a = String("Valve No. ") + String(selected_id);
  806. String b = String("Runtime ") + String(selected_time) + String('s');
  807. print("----- Confirm? -----",
  808. a.c_str(),
  809. b.c_str(),
  810. " # Confirm",
  811. -1);
  812. } else if (s == menu_valves_run) {
  813. unsigned long runtime = millis() - start_time;
  814. String a = String("Time: ") + String(runtime / 1000UL) + String("s / ") + String(selected_time) + String('s');
  815. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  816. String b;
  817. for (unsigned long i = 0; i <= anim; i++) {
  818. b += '#';
  819. }
  820. print("---- Dispensing ----",
  821. a.c_str(),
  822. b.c_str(),
  823. "Hit any key to stop!",
  824. -1);
  825. } else if (s == menu_valves_done) {
  826. String a = String("after ") + String((stop_time - start_time) / 1000UL) + String("s.");
  827. print("------- Done -------",
  828. "Dispensing finished",
  829. a.c_str(),
  830. "Hit any key for menu",
  831. -1);
  832. #if defined(PLATFORM_ESP)
  833. unsigned long runtime = stop_time - start_time;
  834. if (selected_id <= plants.countPlants()) {
  835. bool success = wifi_write_database(runtime / 1000, "plant", selected_id);
  836. if (!success) {
  837. debug.print("Error writing to InfluxDB ");
  838. debug.print(INFLUXDB_HOST);
  839. debug.print(":");
  840. debug.print(INFLUXDB_PORT);
  841. debug.print("/");
  842. debug.print(INFLUXDB_DATABASE);
  843. debug.println("/plant");
  844. }
  845. }
  846. #endif // PLATFORM_ESP
  847. } else if (s == error) {
  848. print("------ Error! ------",
  849. "There is a problem:",
  850. error_condition.c_str(),
  851. " Press any key...",
  852. -1);
  853. } else {
  854. debug.print("Invalid state ");
  855. debug.println(s);
  856. }
  857. }