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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085
  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. #include "config_pins.h"
  25. #ifdef FUNCTION_CONTROL
  26. Statemachine::DigitBuffer::DigitBuffer(int _size) {
  27. size = _size;
  28. pos = 0;
  29. digits = new int[size];
  30. }
  31. Statemachine::DigitBuffer::~DigitBuffer() {
  32. delete digits;
  33. }
  34. bool Statemachine::DigitBuffer::spaceLeft(void) {
  35. return (pos < size);
  36. }
  37. bool Statemachine::DigitBuffer::hasDigits(void) {
  38. return (pos > 0);
  39. }
  40. int Statemachine::DigitBuffer::countDigits(void) {
  41. return pos;
  42. }
  43. void Statemachine::DigitBuffer::addDigit(int d) {
  44. if (spaceLeft()) {
  45. digits[pos] = d;
  46. pos++;
  47. }
  48. }
  49. void Statemachine::DigitBuffer::removeDigit(void) {
  50. if (hasDigits()) {
  51. pos--;
  52. }
  53. }
  54. void Statemachine::DigitBuffer::clear(void) {
  55. pos = 0;
  56. }
  57. uint32_t Statemachine::DigitBuffer::getNumber(void) {
  58. uint32_t fact = 1;
  59. uint32_t sum = 0;
  60. for (int i = (pos - 1); i >= 0; i--) {
  61. sum += digits[i] * fact;
  62. fact *= 10;
  63. }
  64. return sum;
  65. }
  66. static const char *state_names[] = {
  67. stringify(init),
  68. stringify(door_select),
  69. stringify(menu_a),
  70. stringify(menu_b),
  71. stringify(menu_c),
  72. stringify(auto_mode_a),
  73. stringify(auto_mode_b),
  74. stringify(auto_mode_c),
  75. stringify(auto_fert_a),
  76. stringify(auto_fert_b),
  77. stringify(auto_fert_run),
  78. stringify(auto_tank_run),
  79. stringify(auto_stirr_run),
  80. stringify(auto_plant),
  81. stringify(auto_plant_kickstart_run),
  82. stringify(auto_plant_run),
  83. stringify(auto_done),
  84. stringify(fillnwater_plant),
  85. stringify(fillnwater_tank_run),
  86. stringify(fillnwater_kickstart_run),
  87. stringify(fillnwater_plant_run),
  88. stringify(fullauto_fert),
  89. stringify(fullauto_plant),
  90. stringify(fullauto_stirr_run),
  91. stringify(fullauto_fert_run),
  92. stringify(fullauto_tank_run),
  93. stringify(fullauto_kickstart_run),
  94. stringify(fullauto_plant_run),
  95. stringify(fullauto_plant_overrun),
  96. stringify(fullauto_tank_purge_run),
  97. stringify(fullauto_kickstart_purge_run),
  98. stringify(fullauto_plant_purge_run),
  99. stringify(fullauto_plant_purge_overrun),
  100. stringify(fullauto_done),
  101. stringify(automation_mode),
  102. stringify(menu_pumps),
  103. stringify(menu_pumps_time),
  104. stringify(menu_pumps_go),
  105. stringify(menu_pumps_run),
  106. stringify(menu_pumps_done),
  107. stringify(menu_valves),
  108. stringify(menu_valves_time),
  109. stringify(menu_valves_go),
  110. stringify(menu_valves_run),
  111. stringify(menu_valves_done),
  112. stringify(menu_aux),
  113. stringify(menu_aux_time),
  114. stringify(menu_aux_go),
  115. stringify(menu_aux_run),
  116. stringify(menu_aux_done),
  117. stringify(error)
  118. };
  119. static int auto_pump_runtime[PUMP_COUNT] = AUTO_PUMP_RUNTIME;
  120. const char *Statemachine::getStateName(void) {
  121. return state_names[state];
  122. }
  123. bool Statemachine::isIdle(void) {
  124. return state == init;
  125. }
  126. Statemachine::Statemachine(print_fn _print, backspace_fn _backspace)
  127. : db(7), selected_plants(plants.countPlants()),
  128. selected_ferts(plants.countFertilizers()) {
  129. state = init;
  130. old_state = init;
  131. print = _print;
  132. backspace = _backspace;
  133. selected_id = 0;
  134. selected_time = 0;
  135. start_time = 0;
  136. stop_time = 0;
  137. last_animation_time = 0;
  138. error_condition = "";
  139. into_state_time = 0;
  140. filling_started_empty = false;
  141. watering_started_full = false;
  142. menu_entered_digits = "";
  143. }
  144. void Statemachine::begin(void) {
  145. switch_to(init);
  146. }
  147. void Statemachine::input(int n) {
  148. if (state == init) {
  149. #if (LOCK_COUNT > 0) && defined(DOOR_LOCK_PIN)
  150. if (n == -1) {
  151. if (db.hasDigits()) {
  152. backspace();
  153. db.removeDigit();
  154. if (menu_entered_digits.length() > 0) {
  155. menu_entered_digits.remove(menu_entered_digits.length() - 1);
  156. switch_to(state);
  157. }
  158. } else {
  159. switch_to(menu_a);
  160. }
  161. } else if (n == -2) {
  162. switch_to(menu_a);
  163. } else {
  164. if (db.spaceLeft()) {
  165. db.addDigit(n);
  166. //menu_entered_digits += String(n);
  167. menu_entered_digits += String("*");
  168. switch_to(state);
  169. } else {
  170. backspace();
  171. }
  172. uint32_t n = db.getNumber();
  173. if (n == DOOR_LOCK_PIN) {
  174. db.clear();
  175. menu_entered_digits = "";
  176. selected_plants.clear();
  177. switch_to(door_select);
  178. } else if (db.countDigits() >= DOOR_LOCK_PIN_MAX_DIGITS) {
  179. db.clear();
  180. menu_entered_digits = "";
  181. switch_to(state);
  182. }
  183. }
  184. #else
  185. switch_to(menu_a);
  186. #endif
  187. } else if (state == door_select) {
  188. #if (LOCK_COUNT > 0)
  189. if (n == -1) {
  190. if (db.hasDigits()) {
  191. backspace();
  192. db.removeDigit();
  193. if (menu_entered_digits.length() > 0) {
  194. menu_entered_digits.remove(menu_entered_digits.length() - 1);
  195. switch_to(state);
  196. }
  197. } else {
  198. switch_to(init);
  199. }
  200. } else if (n == -2) {
  201. if (!db.hasDigits()) {
  202. for (int i = 0; i < LOCK_COUNT; i++) {
  203. if (selected_plants.isSet(i)) {
  204. plants.startAux(STIRRER_COUNT + i);
  205. delay(DOOR_LOCK_ON_TIME);
  206. plants.stopAux(STIRRER_COUNT + i);
  207. delay(DOOR_LOCK_NEXT_DELAY);
  208. }
  209. }
  210. plants.stopAllAux();
  211. switch_to(menu_a);
  212. } else {
  213. selected_id = number_input();
  214. if ((selected_id <= 0) || (selected_id > LOCK_COUNT)) {
  215. error_condition = F("Invalid lock ID!");
  216. switch_to(error);
  217. } else {
  218. selected_plants.set(selected_id - 1);
  219. menu_entered_digits = "";
  220. switch_to(state);
  221. }
  222. }
  223. } else {
  224. if (db.spaceLeft()) {
  225. db.addDigit(n);
  226. menu_entered_digits += String(n);
  227. switch_to(state);
  228. } else {
  229. backspace();
  230. }
  231. }
  232. #else
  233. // should never be reached
  234. switch_to(menu_a);
  235. #endif
  236. } else if ((state == menu_a) || (state == menu_b) || (state == menu_c)) {
  237. if (n == 1) {
  238. switch_to(auto_mode_a);
  239. } else if (n == 2) {
  240. switch_to(automation_mode);
  241. } else if (n == 3) {
  242. switch_to(menu_pumps);
  243. } else if (n == 4) {
  244. switch_to(menu_valves);
  245. } else if (n == 5) {
  246. switch_to(menu_aux);
  247. #if (LOCK_COUNT > 0) && !defined(DOOR_LOCK_PIN)
  248. } else if (n == 6) {
  249. switch_to(door_select);
  250. #endif
  251. } else if (n == -1) {
  252. switch_to(init);
  253. } else if (n == -2) {
  254. switch_to((state == menu_a) ? menu_b : ((state == menu_b) ? menu_c : menu_a));
  255. }
  256. } else if (state == automation_mode) {
  257. // TODO
  258. switch_to(menu_a);
  259. } else if ((state == auto_mode_a) || (state == auto_mode_b) || (state == auto_mode_c)) {
  260. if (n == 1) {
  261. selected_ferts.clear();
  262. switch_to(fullauto_fert);
  263. } else if (n == 2) {
  264. switch_to(auto_fert_a);
  265. } else if (n == 3) {
  266. selected_plants.clear();
  267. switch_to(fillnwater_plant);
  268. } else if (n == 4) {
  269. auto wl = plants.getWaterlevel();
  270. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  271. plants.openWaterInlet();
  272. selected_id = plants.countPlants() + 1;
  273. selected_time = MAX_TANK_FILL_TIME;
  274. start_time = millis();
  275. switch_to(auto_tank_run);
  276. } else if (wl == Plants::full) {
  277. stop_time = millis();
  278. switch_to(auto_mode_a);
  279. } else if (wl == Plants::invalid) {
  280. error_condition = F("Invalid sensor state");
  281. state = auto_mode_a;
  282. switch_to(error);
  283. }
  284. } else if (n == 5) {
  285. selected_plants.clear();
  286. switch_to(auto_plant);
  287. } else if (n == -1) {
  288. switch_to(menu_a);
  289. } else if (n == -2) {
  290. switch_to((state == auto_mode_a) ? auto_mode_b : ((state == auto_mode_b) ? auto_mode_c : auto_mode_a));
  291. }
  292. } else if ((state == auto_fert_a) || (state == auto_fert_b)) {
  293. // TODO fertilizer number currently "hardcoded" to 3 in UI
  294. if ((n >= 1) && (n <= 3)) {
  295. auto wl = plants.getWaterlevel();
  296. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  297. plants.startFertilizer(n - 1);
  298. selected_id = n;
  299. selected_time = auto_pump_runtime[n - 1];
  300. start_time = millis();
  301. switch_to(auto_fert_run);
  302. } else if (wl == Plants::full) {
  303. stop_time = millis();
  304. switch_to(auto_mode_a);
  305. } else if (wl == Plants::invalid) {
  306. error_condition = F("Invalid sensor state");
  307. state = auto_mode_a;
  308. switch_to(error);
  309. }
  310. } else if (n == 4) {
  311. for (int i = 0; i < STIRRER_COUNT; i++) {
  312. plants.startAux(i);
  313. }
  314. selected_id = 1;
  315. selected_time = AUTO_STIRR_RUNTIME;
  316. start_time = millis();
  317. switch_to(auto_stirr_run);
  318. } else if (n == -1) {
  319. switch_to(auto_mode_a);
  320. } else if (n == -2) {
  321. switch_to((state == auto_fert_a) ? auto_fert_b : auto_fert_a);
  322. }
  323. } else if (state == fullauto_stirr_run) {
  324. // allow user to stop stirring and continue with fertilizers
  325. plants.abort();
  326. stop_time = millis();
  327. if (selected_ferts.countSet() > 0) {
  328. selected_time = auto_pump_runtime[0];
  329. for (int i = 0; i < plants.countFertilizers(); i++) {
  330. if (auto_pump_runtime[i] > selected_time) {
  331. selected_time = auto_pump_runtime[i];
  332. }
  333. if (selected_ferts.isSet(i)) {
  334. plants.startFertilizer(i);
  335. }
  336. }
  337. start_time = millis();
  338. switch_to(fullauto_fert_run);
  339. } else {
  340. auto wl = plants.getWaterlevel();
  341. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  342. // if the waterlevel is currently empty, we
  343. // set a flag to record the time to fill
  344. filling_started_empty = (wl == Plants::empty);
  345. plants.openWaterInlet();
  346. selected_id = plants.countPlants() + 1;
  347. selected_time = MAX_TANK_FILL_TIME;
  348. start_time = millis();
  349. switch_to(fullauto_tank_run);
  350. } else if (wl == Plants::full) {
  351. // check if kickstart is required for this
  352. bool need_kickstart = false;
  353. for (int i = 0; i < plants.countPlants(); i++) {
  354. if (selected_plants.isSet(i)) {
  355. if (plants.getKickstart()->getPinNumber(i) >= 0) {
  356. need_kickstart = true;
  357. }
  358. }
  359. }
  360. // start kickstart/valve as needed
  361. for (int i = 0; i < plants.countPlants(); i++) {
  362. if (selected_plants.isSet(i)) {
  363. plants.startPlant(i, need_kickstart);
  364. }
  365. }
  366. watering_started_full = (wl == Plants::full);
  367. selected_time = MAX_AUTO_PLANT_RUNTIME;
  368. start_time = millis();
  369. if (need_kickstart) {
  370. switch_to(fullauto_kickstart_run);
  371. } else {
  372. switch_to(fullauto_plant_run);
  373. }
  374. } else if (wl == Plants::invalid) {
  375. plants.abort();
  376. error_condition = F("Invalid sensor state");
  377. state = fullauto_fert;
  378. switch_to(error);
  379. }
  380. }
  381. } else if (state == fullauto_fert_run) {
  382. // allow user to stop fertizilers and continue with tank filling
  383. plants.abort();
  384. stop_time = millis();
  385. auto wl = plants.getWaterlevel();
  386. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  387. // if the waterlevel is currently empty, we
  388. // set a flag to record the time to fill
  389. filling_started_empty = (wl == Plants::empty);
  390. plants.openWaterInlet();
  391. selected_id = plants.countPlants() + 1;
  392. selected_time = MAX_TANK_FILL_TIME;
  393. start_time = millis();
  394. switch_to(fullauto_tank_run);
  395. } else if (wl == Plants::full) {
  396. // check if kickstart is required for this
  397. bool need_kickstart = false;
  398. for (int i = 0; i < plants.countPlants(); i++) {
  399. if (selected_plants.isSet(i)) {
  400. if (plants.getKickstart()->getPinNumber(i) >= 0) {
  401. need_kickstart = true;
  402. }
  403. }
  404. }
  405. // start kickstart/valve as needed
  406. for (int i = 0; i < plants.countPlants(); i++) {
  407. if (selected_plants.isSet(i)) {
  408. plants.startPlant(i, need_kickstart);
  409. }
  410. }
  411. watering_started_full = (wl == Plants::full);
  412. selected_time = MAX_AUTO_PLANT_RUNTIME;
  413. start_time = millis();
  414. if (need_kickstart) {
  415. switch_to(fullauto_kickstart_run);
  416. } else {
  417. switch_to(fullauto_plant_run);
  418. }
  419. } else if (wl == Plants::invalid) {
  420. plants.abort();
  421. error_condition = F("Invalid sensor state");
  422. state = fullauto_fert;
  423. switch_to(error);
  424. }
  425. } else if ((state == fullauto_tank_run)
  426. || (state == fullauto_tank_purge_run)) {
  427. // allow user to stop tank and continue with kickstart
  428. plants.abort();
  429. stop_time = millis();
  430. auto wl = plants.getWaterlevel();
  431. if ((wl != Plants::empty) && (wl != Plants::invalid)) {
  432. // check if kickstart is required for this
  433. bool need_kickstart = false;
  434. for (int i = 0; i < plants.countPlants(); i++) {
  435. if (selected_plants.isSet(i)) {
  436. if (plants.getKickstart()->getPinNumber(i) >= 0) {
  437. need_kickstart = true;
  438. }
  439. }
  440. }
  441. // start kickstart/valve as needed
  442. for (int i = 0; i < plants.countPlants(); i++) {
  443. if (selected_plants.isSet(i)) {
  444. plants.startPlant(i, need_kickstart);
  445. }
  446. }
  447. watering_started_full = (wl == Plants::full);
  448. selected_time = MAX_AUTO_PLANT_RUNTIME;
  449. start_time = millis();
  450. if (need_kickstart) {
  451. if (state == fullauto_tank_run) {
  452. switch_to(fullauto_kickstart_run);
  453. } else {
  454. switch_to(fullauto_kickstart_purge_run);
  455. }
  456. } else {
  457. if (state == fullauto_tank_run) {
  458. switch_to(fullauto_plant_run);
  459. } else {
  460. switch_to(fullauto_plant_purge_run);
  461. }
  462. }
  463. } else if (wl == Plants::empty) {
  464. stop_time = millis();
  465. switch_to(auto_done);
  466. } else if (wl == Plants::invalid) {
  467. error_condition = F("Invalid sensor state");
  468. state = auto_mode_a;
  469. switch_to(error);
  470. }
  471. } else if ((state == fullauto_kickstart_run)
  472. || (state == fullauto_kickstart_purge_run)) {
  473. // allow user to stop kickstarting and continue with plants
  474. plants.abort();
  475. selected_time = MAX_AUTO_PLANT_RUNTIME;
  476. start_time = millis();
  477. // start required valves
  478. for (int i = 0; i < plants.countPlants(); i++) {
  479. if (selected_plants.isSet(i)) {
  480. plants.startPlant(i, false);
  481. }
  482. }
  483. if (state == fullauto_kickstart_run) {
  484. switch_to(fullauto_plant_run);
  485. } else {
  486. switch_to(fullauto_plant_purge_run);
  487. }
  488. } else if ((state == auto_fert_run) || (state == auto_tank_run)
  489. || (state == auto_stirr_run) || (state == auto_plant_run)
  490. || (state == auto_plant_kickstart_run)
  491. || (state == fillnwater_kickstart_run)
  492. || (state == fullauto_plant_run)
  493. || (state == fullauto_plant_overrun)
  494. || (state == fullauto_plant_purge_run)
  495. || (state == fullauto_plant_purge_overrun)) {
  496. plants.abort();
  497. stop_time = millis();
  498. switch_to(auto_done);
  499. } else if (state == auto_plant) {
  500. if (n == -1) {
  501. if (db.hasDigits()) {
  502. backspace();
  503. db.removeDigit();
  504. if (menu_entered_digits.length() > 0) {
  505. menu_entered_digits.remove(menu_entered_digits.length() - 1);
  506. switch_to(state);
  507. }
  508. } else {
  509. switch_to(auto_mode_b);
  510. }
  511. } else if (n == -2) {
  512. if (!db.hasDigits()) {
  513. auto wl = plants.getWaterlevel();
  514. if ((wl != Plants::empty) && (wl != Plants::invalid)) {
  515. // check if kickstart is required for this
  516. bool need_kickstart = false;
  517. for (int i = 0; i < plants.countPlants(); i++) {
  518. if (selected_plants.isSet(i)) {
  519. if (plants.getKickstart()->getPinNumber(i) >= 0) {
  520. need_kickstart = true;
  521. }
  522. }
  523. }
  524. // start kickstart/valve as needed
  525. for (int i = 0; i < plants.countPlants(); i++) {
  526. if (selected_plants.isSet(i)) {
  527. plants.startPlant(i, need_kickstart);
  528. }
  529. }
  530. selected_time = MAX_AUTO_PLANT_RUNTIME;
  531. start_time = millis();
  532. if (need_kickstart) {
  533. switch_to(auto_plant_kickstart_run);
  534. } else {
  535. switch_to(auto_plant_run);
  536. }
  537. } else if (wl == Plants::empty) {
  538. stop_time = millis();
  539. switch_to(auto_mode_b);
  540. } else if (wl == Plants::invalid) {
  541. error_condition = F("Invalid sensor state");
  542. state = auto_mode_b;
  543. switch_to(error);
  544. }
  545. } else {
  546. selected_id = number_input();
  547. if ((selected_id <= 0) || (selected_id > plants.countPlants())) {
  548. error_condition = F("Invalid plant ID!");
  549. switch_to(error);
  550. } else {
  551. selected_plants.set(selected_id - 1);
  552. menu_entered_digits = "";
  553. switch_to(auto_plant);
  554. }
  555. }
  556. } else {
  557. if (db.spaceLeft()) {
  558. db.addDigit(n);
  559. menu_entered_digits += String(n);
  560. switch_to(state);
  561. } else {
  562. backspace();
  563. }
  564. }
  565. } else if (state == fullauto_fert) {
  566. if (n == -1) {
  567. if (db.hasDigits()) {
  568. backspace();
  569. db.removeDigit();
  570. if (menu_entered_digits.length() > 0) {
  571. menu_entered_digits.remove(menu_entered_digits.length() - 1);
  572. switch_to(state);
  573. }
  574. } else {
  575. switch_to(auto_mode_a);
  576. }
  577. } else if (n == -2) {
  578. if (!db.hasDigits()) {
  579. selected_plants.clear();
  580. switch_to(fullauto_plant);
  581. } else {
  582. selected_id = number_input();
  583. if ((selected_id <= 0) || (selected_id > plants.countFertilizers())) {
  584. error_condition = F("Invalid fert. ID!");
  585. switch_to(error);
  586. } else {
  587. selected_ferts.set(selected_id - 1);
  588. menu_entered_digits = "";
  589. switch_to(fullauto_fert);
  590. }
  591. }
  592. } else {
  593. if (db.spaceLeft()) {
  594. db.addDigit(n);
  595. menu_entered_digits += String(n);
  596. switch_to(state);
  597. } else {
  598. backspace();
  599. }
  600. }
  601. } else if (state == fullauto_plant) {
  602. if (n == -1) {
  603. if (db.hasDigits()) {
  604. backspace();
  605. db.removeDigit();
  606. if (menu_entered_digits.length() > 0) {
  607. menu_entered_digits.remove(menu_entered_digits.length() - 1);
  608. switch_to(state);
  609. }
  610. } else {
  611. selected_ferts.clear();
  612. switch_to(fullauto_fert);
  613. }
  614. } else if (n == -2) {
  615. if (!db.hasDigits()) {
  616. if (selected_plants.countSet() <= 0) {
  617. // user has not selected a plant yet...
  618. return;
  619. }
  620. // check if we need to run fertilizers
  621. if (selected_ferts.countSet() > 0) {
  622. // stirr before pumping fertilizers
  623. for (int i = 0; i < STIRRER_COUNT; i++) {
  624. plants.startAux(i);
  625. }
  626. selected_id = 1;
  627. selected_time = AUTO_STIRR_RUNTIME;
  628. start_time = millis();
  629. switch_to(fullauto_stirr_run);
  630. } else {
  631. // immediately continue with filling tank
  632. auto wl = plants.getWaterlevel();
  633. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  634. // if the waterlevel is currently empty, we
  635. // set a flag to record the time to fill
  636. filling_started_empty = (wl == Plants::empty);
  637. plants.openWaterInlet();
  638. selected_id = plants.countPlants() + 1;
  639. selected_time = MAX_TANK_FILL_TIME;
  640. start_time = millis();
  641. switch_to(fullauto_tank_run);
  642. } else if (wl == Plants::full) {
  643. // check if kickstart is required for this
  644. bool need_kickstart = false;
  645. for (int i = 0; i < plants.countPlants(); i++) {
  646. if (selected_plants.isSet(i)) {
  647. if (plants.getKickstart()->getPinNumber(i) >= 0) {
  648. need_kickstart = true;
  649. }
  650. }
  651. }
  652. // start kickstart/valve as needed
  653. for (int i = 0; i < plants.countPlants(); i++) {
  654. if (selected_plants.isSet(i)) {
  655. plants.startPlant(i, need_kickstart);
  656. }
  657. }
  658. // for recording the flowrate
  659. watering_started_full = (wl == Plants::full);
  660. selected_time = MAX_AUTO_PLANT_RUNTIME;
  661. start_time = millis();
  662. if (need_kickstart) {
  663. switch_to(fullauto_kickstart_run);
  664. } else {
  665. switch_to(fullauto_plant_run);
  666. }
  667. } else if (wl == Plants::invalid) {
  668. error_condition = F("Invalid sensor state");
  669. state = auto_mode_a;
  670. switch_to(error);
  671. }
  672. }
  673. } else {
  674. selected_id = number_input();
  675. if ((selected_id <= 0) || (selected_id > plants.countPlants())) {
  676. error_condition = F("Invalid plant ID!");
  677. switch_to(error);
  678. } else {
  679. selected_plants.set(selected_id - 1);
  680. menu_entered_digits = "";
  681. switch_to(fullauto_plant);
  682. }
  683. }
  684. } else {
  685. if (db.spaceLeft()) {
  686. db.addDigit(n);
  687. menu_entered_digits += String(n);
  688. switch_to(state);
  689. } else {
  690. backspace();
  691. }
  692. }
  693. } else if ((state == auto_done) || (state == fullauto_done)) {
  694. switch_to(auto_mode_a);
  695. } else if (state == menu_pumps) {
  696. if (n == -1) {
  697. if (db.hasDigits()) {
  698. backspace();
  699. db.removeDigit();
  700. if (menu_entered_digits.length() > 0) {
  701. menu_entered_digits.remove(menu_entered_digits.length() - 1);
  702. switch_to(state);
  703. }
  704. } else {
  705. switch_to(menu_b);
  706. }
  707. } else if (n == -2) {
  708. if (!db.hasDigits()) {
  709. return;
  710. }
  711. selected_id = number_input();
  712. if ((selected_id <= 0) || (selected_id > plants.countFertilizers())) {
  713. error_condition = F("Invalid pump ID!");
  714. switch_to(error);
  715. } else {
  716. switch_to(menu_pumps_time);
  717. }
  718. } else {
  719. if (db.spaceLeft()) {
  720. db.addDigit(n);
  721. menu_entered_digits += String(n);
  722. switch_to(state);
  723. } else {
  724. backspace();
  725. }
  726. }
  727. } else if (state == fillnwater_plant) {
  728. if (n == -1) {
  729. if (db.hasDigits()) {
  730. backspace();
  731. db.removeDigit();
  732. if (menu_entered_digits.length() > 0) {
  733. menu_entered_digits.remove(menu_entered_digits.length() - 1);
  734. switch_to(state);
  735. }
  736. } else {
  737. switch_to(auto_mode_a);
  738. }
  739. } else if (n == -2) {
  740. if (!db.hasDigits()) {
  741. if (selected_plants.countSet() <= 0) {
  742. return;
  743. }
  744. auto wl = plants.getWaterlevel();
  745. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  746. // if the waterlevel is currently empty, we
  747. // set a flag to record the time to fill
  748. filling_started_empty = (wl == Plants::empty);
  749. plants.openWaterInlet();
  750. selected_id = plants.countPlants() + 1;
  751. selected_time = MAX_TANK_FILL_TIME;
  752. start_time = millis();
  753. switch_to(fillnwater_tank_run);
  754. } else if (wl == Plants::full) {
  755. // check if kickstart is required for this
  756. bool need_kickstart = false;
  757. for (int i = 0; i < plants.countPlants(); i++) {
  758. if (selected_plants.isSet(i)) {
  759. if (plants.getKickstart()->getPinNumber(i) >= 0) {
  760. need_kickstart = true;
  761. }
  762. }
  763. }
  764. // start kickstart/valve as needed
  765. for (int i = 0; i < plants.countPlants(); i++) {
  766. if (selected_plants.isSet(i)) {
  767. plants.startPlant(i, need_kickstart);
  768. }
  769. }
  770. // for recording the flowrate
  771. watering_started_full = (wl == Plants::full);
  772. selected_time = MAX_AUTO_PLANT_RUNTIME;
  773. start_time = millis();
  774. if (need_kickstart) {
  775. switch_to(fillnwater_kickstart_run);
  776. } else {
  777. switch_to(fillnwater_plant_run);
  778. }
  779. } else if (wl == Plants::invalid) {
  780. error_condition = F("Invalid sensor state");
  781. state = auto_mode_a;
  782. switch_to(error);
  783. }
  784. } else {
  785. selected_id = number_input();
  786. if ((selected_id <= 0) || (selected_id > plants.countPlants())) {
  787. error_condition = F("Invalid plant ID!");
  788. switch_to(error);
  789. } else {
  790. selected_plants.set(selected_id - 1);
  791. menu_entered_digits = "";
  792. switch_to(fillnwater_plant);
  793. }
  794. }
  795. } else {
  796. if (db.spaceLeft()) {
  797. db.addDigit(n);
  798. menu_entered_digits += String(n);
  799. switch_to(state);
  800. } else {
  801. backspace();
  802. }
  803. }
  804. } else if (state == fillnwater_tank_run) {
  805. plants.abort();
  806. stop_time = millis();
  807. auto wl = plants.getWaterlevel();
  808. if ((wl != Plants::empty) && (wl != Plants::invalid)) {
  809. // check if kickstart is required for this
  810. bool need_kickstart = false;
  811. for (int i = 0; i < plants.countPlants(); i++) {
  812. if (selected_plants.isSet(i)) {
  813. if (plants.getKickstart()->getPinNumber(i) >= 0) {
  814. need_kickstart = true;
  815. }
  816. }
  817. }
  818. // start kickstart/valve as needed
  819. for (int i = 0; i < plants.countPlants(); i++) {
  820. if (selected_plants.isSet(i)) {
  821. plants.startPlant(i, need_kickstart);
  822. }
  823. }
  824. watering_started_full = (wl == Plants::full);
  825. selected_time = MAX_AUTO_PLANT_RUNTIME;
  826. start_time = millis();
  827. if (need_kickstart) {
  828. switch_to(fillnwater_kickstart_run);
  829. } else {
  830. switch_to(fillnwater_plant_run);
  831. }
  832. } else if (wl == Plants::empty) {
  833. switch_to(auto_mode_a);
  834. } else if (wl == Plants::invalid) {
  835. error_condition = F("Invalid sensor state");
  836. state = auto_mode_a;
  837. switch_to(error);
  838. }
  839. } else if (state == fillnwater_plant_run) {
  840. plants.abort();
  841. stop_time = millis();
  842. switch_to(auto_done);
  843. } else if (state == menu_pumps_time) {
  844. if (n == -1) {
  845. if (db.hasDigits()) {
  846. backspace();
  847. db.removeDigit();
  848. if (menu_entered_digits.length() > 0) {
  849. menu_entered_digits.remove(menu_entered_digits.length() - 1);
  850. switch_to(state);
  851. }
  852. } else {
  853. switch_to(menu_pumps);
  854. }
  855. } else if (n == -2) {
  856. if (!db.hasDigits()) {
  857. return;
  858. }
  859. selected_time = number_input();
  860. if ((selected_time <= 0) || (selected_time > MAX_PUMP_RUNTIME)) {
  861. error_condition = F("Invalid time range!");
  862. switch_to(error);
  863. } else {
  864. switch_to(menu_pumps_go);
  865. }
  866. } else {
  867. if (db.spaceLeft()) {
  868. db.addDigit(n);
  869. menu_entered_digits += String(n);
  870. switch_to(state);
  871. } else {
  872. backspace();
  873. }
  874. }
  875. } else if (state == menu_pumps_go) {
  876. if (n == -2) {
  877. start_time = millis();
  878. last_animation_time = start_time;
  879. auto wl = plants.getWaterlevel();
  880. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  881. plants.startFertilizer(selected_id - 1);
  882. switch_to(menu_pumps_run);
  883. } else if (wl == Plants::full) {
  884. stop_time = millis();
  885. switch_to(menu_pumps_done);
  886. } else if (wl == Plants::invalid) {
  887. error_condition = F("Invalid sensor state");
  888. state = menu_pumps;
  889. switch_to(error);
  890. }
  891. } else {
  892. switch_to(menu_pumps_time);
  893. }
  894. } else if (state == menu_pumps_run) {
  895. plants.abort();
  896. stop_time = millis();
  897. switch_to(menu_pumps_done);
  898. } else if (state == menu_pumps_done) {
  899. switch_to(menu_b);
  900. } else if (state == menu_valves) {
  901. if (n == -1) {
  902. if (db.hasDigits()) {
  903. backspace();
  904. db.removeDigit();
  905. if (menu_entered_digits.length() > 0) {
  906. menu_entered_digits.remove(menu_entered_digits.length() - 1);
  907. switch_to(state);
  908. }
  909. } else {
  910. switch_to(menu_b);
  911. }
  912. } else if (n == -2) {
  913. if (!db.hasDigits()) {
  914. return;
  915. }
  916. selected_id = number_input();
  917. if ((selected_id <= 0) || (selected_id > (plants.countPlants() + 1))) {
  918. error_condition = F("Invalid valve ID!");
  919. switch_to(error);
  920. } else {
  921. switch_to(menu_valves_time);
  922. }
  923. } else {
  924. if (db.spaceLeft()) {
  925. db.addDigit(n);
  926. menu_entered_digits += String(n);
  927. switch_to(state);
  928. } else {
  929. backspace();
  930. }
  931. }
  932. } else if (state == menu_valves_time) {
  933. if (n == -1) {
  934. if (db.hasDigits()) {
  935. backspace();
  936. db.removeDigit();
  937. if (menu_entered_digits.length() > 0) {
  938. menu_entered_digits.remove(menu_entered_digits.length() - 1);
  939. switch_to(state);
  940. }
  941. } else {
  942. switch_to(menu_valves);
  943. }
  944. } else if (n == -2) {
  945. if (!db.hasDigits()) {
  946. return;
  947. }
  948. selected_time = number_input();
  949. if ((selected_time <= 0) || (selected_time > MAX_VALVE_RUNTIME)) {
  950. error_condition = F("Invalid time range!");
  951. switch_to(error);
  952. } else {
  953. switch_to(menu_valves_go);
  954. }
  955. } else {
  956. if (db.spaceLeft()) {
  957. db.addDigit(n);
  958. menu_entered_digits += String(n);
  959. switch_to(state);
  960. } else {
  961. backspace();
  962. }
  963. }
  964. } else if (state == menu_valves_go) {
  965. if (n == -2) {
  966. start_time = millis();
  967. last_animation_time = start_time;
  968. auto wl = plants.getWaterlevel();
  969. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  970. if (selected_id >= (plants.countPlants() + 1)) {
  971. plants.openWaterInlet();
  972. } else {
  973. // TODO support testing kickstart
  974. plants.startPlant(selected_id - 1, false);
  975. }
  976. switch_to(menu_valves_run);
  977. } else if (wl == Plants::full) {
  978. stop_time = millis();
  979. switch_to(menu_valves_done);
  980. } else if (wl == Plants::invalid) {
  981. error_condition = F("Invalid sensor state");
  982. state = menu_valves;
  983. switch_to(error);
  984. }
  985. } else {
  986. switch_to(menu_valves_time);
  987. }
  988. } else if (state == menu_valves_run) {
  989. plants.abort();
  990. stop_time = millis();
  991. switch_to(menu_valves_done);
  992. } else if (state == menu_valves_done) {
  993. switch_to(menu_b);
  994. } else if (state == menu_aux) {
  995. if (n == -1) {
  996. if (db.hasDigits()) {
  997. backspace();
  998. db.removeDigit();
  999. if (menu_entered_digits.length() > 0) {
  1000. menu_entered_digits.remove(menu_entered_digits.length() - 1);
  1001. switch_to(state);
  1002. }
  1003. } else {
  1004. switch_to(menu_c);
  1005. }
  1006. } else if (n == -2) {
  1007. if (!db.hasDigits()) {
  1008. return;
  1009. }
  1010. selected_id = number_input();
  1011. if ((selected_id <= 0) || (selected_id > plants.countAux())) {
  1012. error_condition = F("Invalid valve ID!");
  1013. switch_to(error);
  1014. } else {
  1015. switch_to(menu_aux_time);
  1016. }
  1017. } else {
  1018. if (db.spaceLeft()) {
  1019. db.addDigit(n);
  1020. menu_entered_digits += String(n);
  1021. switch_to(state);
  1022. } else {
  1023. backspace();
  1024. }
  1025. }
  1026. } else if (state == menu_aux_time) {
  1027. if (n == -1) {
  1028. if (db.hasDigits()) {
  1029. backspace();
  1030. db.removeDigit();
  1031. if (menu_entered_digits.length() > 0) {
  1032. menu_entered_digits.remove(menu_entered_digits.length() - 1);
  1033. switch_to(state);
  1034. }
  1035. } else {
  1036. switch_to(menu_aux);
  1037. }
  1038. } else if (n == -2) {
  1039. if (!db.hasDigits()) {
  1040. return;
  1041. }
  1042. selected_time = number_input();
  1043. if ((selected_time <= 0) || (selected_time > MAX_AUX_RUNTIME)) {
  1044. error_condition = F("Invalid time range!");
  1045. switch_to(error);
  1046. } else {
  1047. switch_to(menu_aux_go);
  1048. }
  1049. } else {
  1050. if (db.spaceLeft()) {
  1051. db.addDigit(n);
  1052. menu_entered_digits += String(n);
  1053. switch_to(state);
  1054. } else {
  1055. backspace();
  1056. }
  1057. }
  1058. } else if (state == menu_aux_go) {
  1059. if (n == -2) {
  1060. start_time = millis();
  1061. last_animation_time = start_time;
  1062. plants.startAux(selected_id - 1);
  1063. switch_to(menu_aux_run);
  1064. } else {
  1065. switch_to(menu_aux_time);
  1066. }
  1067. } else if (state == menu_aux_run) {
  1068. plants.abort();
  1069. stop_time = millis();
  1070. switch_to(menu_aux_done);
  1071. } else if (state == menu_aux_done) {
  1072. switch_to(menu_c);
  1073. } else if (state == error) {
  1074. if (old_state != error) {
  1075. switch_to(old_state);
  1076. } else {
  1077. switch_to(menu_a);
  1078. }
  1079. }
  1080. }
  1081. uint32_t Statemachine::number_input(void) {
  1082. for (int i = 0; i < db.countDigits(); i++) {
  1083. backspace();
  1084. }
  1085. uint32_t n = db.getNumber();
  1086. db.clear();
  1087. debug.print("Whole number input: ");
  1088. debug.println(n);
  1089. return n;
  1090. }
  1091. void Statemachine::act(void) {
  1092. if ((state == menu_pumps_run) || (state == menu_valves_run)
  1093. || (state == menu_aux_run) || (state == fullauto_stirr_run)
  1094. || (state == auto_stirr_run)) {
  1095. unsigned long runtime = millis() - start_time;
  1096. if ((runtime / 1000UL) >= selected_time) {
  1097. // stop if timeout has been reached
  1098. plants.abort();
  1099. stop_time = millis();
  1100. if (state == menu_pumps_run) {
  1101. switch_to(menu_pumps_done);
  1102. } else if (state == menu_valves_run) {
  1103. switch_to(menu_valves_done);
  1104. } else if (state == menu_aux_run) {
  1105. switch_to(menu_aux_done);
  1106. } else if (state == fullauto_stirr_run) {
  1107. if (selected_ferts.countSet() > 0) {
  1108. selected_time = auto_pump_runtime[0];
  1109. for (int i = 0; i < plants.countFertilizers(); i++) {
  1110. if (auto_pump_runtime[i] > selected_time) {
  1111. selected_time = auto_pump_runtime[i];
  1112. }
  1113. if (selected_ferts.isSet(i)) {
  1114. plants.startFertilizer(i);
  1115. }
  1116. }
  1117. start_time = millis();
  1118. switch_to(fullauto_fert_run);
  1119. } else {
  1120. auto wl = plants.getWaterlevel();
  1121. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  1122. // if the waterlevel is currently empty, we
  1123. // set a flag to record the time to fill
  1124. filling_started_empty = (wl == Plants::empty);
  1125. plants.openWaterInlet();
  1126. selected_id = plants.countPlants() + 1;
  1127. selected_time = MAX_TANK_FILL_TIME;
  1128. start_time = millis();
  1129. switch_to(fullauto_tank_run);
  1130. } else if (wl == Plants::full) {
  1131. // check if kickstart is required for this
  1132. bool need_kickstart = false;
  1133. for (int i = 0; i < plants.countPlants(); i++) {
  1134. if (selected_plants.isSet(i)) {
  1135. if (plants.getKickstart()->getPinNumber(i) >= 0) {
  1136. need_kickstart = true;
  1137. }
  1138. }
  1139. }
  1140. // start kickstart/valve as needed
  1141. for (int i = 0; i < plants.countPlants(); i++) {
  1142. if (selected_plants.isSet(i)) {
  1143. plants.startPlant(i, need_kickstart);
  1144. }
  1145. }
  1146. watering_started_full = (wl == Plants::full);
  1147. selected_time = MAX_AUTO_PLANT_RUNTIME;
  1148. start_time = millis();
  1149. if (need_kickstart) {
  1150. switch_to(fullauto_kickstart_run);
  1151. } else {
  1152. switch_to(fullauto_plant_run);
  1153. }
  1154. } else if (wl == Plants::invalid) {
  1155. plants.abort();
  1156. error_condition = F("Invalid sensor state");
  1157. state = fullauto_fert;
  1158. switch_to(error);
  1159. }
  1160. }
  1161. } else {
  1162. switch_to(auto_done);
  1163. }
  1164. } else if ((millis() - last_animation_time) >= 500) {
  1165. // update animation if needed
  1166. last_animation_time = millis();
  1167. switch_to(state);
  1168. }
  1169. }
  1170. #ifdef CHECK_SENSORS_VALVE_PUMP_MENU_FULL
  1171. if ((state == menu_pumps_run) || ((state == menu_valves_run) && (selected_id == (plants.countPlants() + 1)))) {
  1172. // check water level state
  1173. auto wl = plants.getWaterlevel();
  1174. if (wl == Plants::full) {
  1175. plants.abort();
  1176. stop_time = millis();
  1177. switch_to((state == menu_pumps_run) ? menu_pumps_done : menu_valves_done);
  1178. } else if (wl == Plants::invalid) {
  1179. plants.abort();
  1180. error_condition = F("Invalid sensor state");
  1181. state = (state == menu_pumps_run) ? menu_pumps : menu_valves;
  1182. switch_to(error);
  1183. }
  1184. }
  1185. #endif // CHECK_SENSORS_VALVE_PUMP_MENU_FULL
  1186. #ifdef CHECK_SENSORS_VALVE_PUMP_MENU_EMPTY
  1187. if ((state == menu_valves_run) && (selected_id <= plants.countPlants())) {
  1188. // check water level state
  1189. auto wl = plants.getWaterlevel();
  1190. if (wl == Plants::empty) {
  1191. plants.abort();
  1192. stop_time = millis();
  1193. switch_to(menu_valves_done);
  1194. } else if (wl == Plants::invalid) {
  1195. plants.abort();
  1196. error_condition = F("Invalid sensor state");
  1197. state = menu_valves;
  1198. switch_to(error);
  1199. }
  1200. }
  1201. #endif // CHECK_SENSORS_VALVE_PUMP_MENU_EMPTY
  1202. // kickstart states
  1203. if ((state == auto_plant_kickstart_run) || (state == fillnwater_kickstart_run)
  1204. || (state == fullauto_kickstart_run) || (state == fullauto_kickstart_purge_run)) {
  1205. unsigned long runtime = millis() - start_time;
  1206. if ((runtime / 1000UL) >= KICKSTART_RUNTIME) {
  1207. // kickstart is done, switch over to valves
  1208. plants.abort();
  1209. selected_time = MAX_AUTO_PLANT_RUNTIME;
  1210. start_time = millis();
  1211. // start required valves
  1212. for (int i = 0; i < plants.countPlants(); i++) {
  1213. if (selected_plants.isSet(i)) {
  1214. plants.startPlant(i, false);
  1215. }
  1216. }
  1217. if (state == auto_plant_kickstart_run) {
  1218. switch_to(auto_plant_run);
  1219. } else if (state == fillnwater_kickstart_run) {
  1220. switch_to(fillnwater_plant_run);
  1221. } else if (state == fullauto_kickstart_run) {
  1222. switch_to(fullauto_plant_run);
  1223. } else {
  1224. switch_to(fullauto_plant_purge_run);
  1225. }
  1226. } else if ((millis() - last_animation_time) >= 500) {
  1227. // update animation if needed
  1228. last_animation_time = millis();
  1229. switch_to(state);
  1230. }
  1231. }
  1232. if (state == fullauto_fert_run) {
  1233. unsigned long runtime = millis() - start_time;
  1234. for (int i = 0; i < plants.countFertilizers(); i++) {
  1235. if (selected_ferts.isSet(i)) {
  1236. if ((runtime / 1000UL) >= auto_pump_runtime[i]) {
  1237. plants.stopFertilizer(i);
  1238. selected_ferts.set(i, false);
  1239. }
  1240. }
  1241. }
  1242. if ((millis() - last_animation_time) >= 500) {
  1243. // update animation if needed
  1244. last_animation_time = millis();
  1245. switch_to(state);
  1246. }
  1247. if (selected_ferts.countSet() <= 0) {
  1248. plants.abort();
  1249. auto wl = plants.getWaterlevel();
  1250. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  1251. // if the waterlevel is currently empty, we
  1252. // set a flag to record the time to fill
  1253. filling_started_empty = (wl == Plants::empty);
  1254. plants.openWaterInlet();
  1255. selected_id = plants.countPlants() + 1;
  1256. selected_time = MAX_TANK_FILL_TIME;
  1257. start_time = millis();
  1258. switch_to(fullauto_tank_run);
  1259. } else if (wl == Plants::full) {
  1260. // check if kickstart is required for this
  1261. bool need_kickstart = false;
  1262. for (int i = 0; i < plants.countPlants(); i++) {
  1263. if (selected_plants.isSet(i)) {
  1264. if (plants.getKickstart()->getPinNumber(i) >= 0) {
  1265. need_kickstart = true;
  1266. }
  1267. }
  1268. }
  1269. // start kickstart/valve as needed
  1270. for (int i = 0; i < plants.countPlants(); i++) {
  1271. if (selected_plants.isSet(i)) {
  1272. plants.startPlant(i, need_kickstart);
  1273. }
  1274. }
  1275. // for recording the flowrate
  1276. watering_started_full = (wl == Plants::full);
  1277. selected_time = MAX_AUTO_PLANT_RUNTIME;
  1278. start_time = millis();
  1279. if (need_kickstart) {
  1280. switch_to(fullauto_kickstart_run);
  1281. } else {
  1282. switch_to(fullauto_plant_run);
  1283. }
  1284. } else if (wl == Plants::invalid) {
  1285. error_condition = F("Invalid sensor state");
  1286. state = auto_mode_a;
  1287. switch_to(error);
  1288. }
  1289. }
  1290. }
  1291. // states that fill the tank up
  1292. if ((state == auto_fert_run) || (state == auto_tank_run)
  1293. || (state == fullauto_tank_run) || (state == fullauto_tank_purge_run)
  1294. || (state == fillnwater_tank_run)) {
  1295. unsigned long runtime = millis() - start_time;
  1296. if ((runtime / 1000UL) >= selected_time) {
  1297. // stop if timeout has been reached
  1298. plants.abort();
  1299. stop_time = millis();
  1300. if ((state == fillnwater_tank_run)
  1301. || (state == fullauto_tank_run)
  1302. || (state == fullauto_tank_purge_run)) {
  1303. auto wl = plants.getWaterlevel();
  1304. if ((wl != Plants::empty) && (wl != Plants::invalid)) {
  1305. // check if kickstart is required for this
  1306. bool need_kickstart = false;
  1307. for (int i = 0; i < plants.countPlants(); i++) {
  1308. if (selected_plants.isSet(i)) {
  1309. if (plants.getKickstart()->getPinNumber(i) >= 0) {
  1310. need_kickstart = true;
  1311. }
  1312. }
  1313. }
  1314. // start kickstart/valve as needed
  1315. for (int i = 0; i < plants.countPlants(); i++) {
  1316. if (selected_plants.isSet(i)) {
  1317. plants.startPlant(i, need_kickstart);
  1318. }
  1319. }
  1320. watering_started_full = (wl == Plants::full);
  1321. selected_time = MAX_AUTO_PLANT_RUNTIME;
  1322. start_time = millis();
  1323. if (need_kickstart) {
  1324. if (state == fillnwater_tank_run) {
  1325. switch_to(fillnwater_kickstart_run);
  1326. } else if (state == fullauto_tank_run) {
  1327. switch_to(fullauto_kickstart_run);
  1328. } else {
  1329. switch_to(fullauto_kickstart_purge_run);
  1330. }
  1331. } else {
  1332. if (state == fillnwater_tank_run) {
  1333. switch_to(fillnwater_plant_run);
  1334. } else if (state == fullauto_tank_run) {
  1335. switch_to(fullauto_plant_run);
  1336. } else {
  1337. switch_to(fullauto_plant_purge_run);
  1338. }
  1339. }
  1340. } else if (wl == Plants::empty) {
  1341. stop_time = millis();
  1342. switch_to(auto_done);
  1343. } else if (wl == Plants::invalid) {
  1344. error_condition = F("Invalid sensor state");
  1345. state = auto_mode_a;
  1346. switch_to(error);
  1347. }
  1348. } else {
  1349. switch_to(auto_done);
  1350. }
  1351. } else if ((millis() - last_animation_time) >= 500) {
  1352. // update animation if needed
  1353. last_animation_time = millis();
  1354. switch_to(state);
  1355. }
  1356. // check water level state
  1357. auto wl = plants.getWaterlevel();
  1358. if (wl == Plants::full) {
  1359. plants.abort();
  1360. stop_time = millis();
  1361. if ((state == fillnwater_tank_run)
  1362. || (state == fullauto_tank_run)
  1363. || (state == fullauto_tank_purge_run)) {
  1364. // record time to fill here, if we started with
  1365. // an empty tank at the start of filling
  1366. if (filling_started_empty) {
  1367. unsigned long time_to_fill = stop_time - start_time;
  1368. debug.print("Filling tank took ");
  1369. debug.print(String(time_to_fill));
  1370. debug.println("ms");
  1371. #if defined(PLATFORM_ESP)
  1372. wifi_write_database(time_to_fill, "calibrated_filling", -1);
  1373. #endif // PLATFORM_ESP
  1374. }
  1375. // check if kickstart is required for this
  1376. bool need_kickstart = false;
  1377. for (int i = 0; i < plants.countPlants(); i++) {
  1378. if (selected_plants.isSet(i)) {
  1379. if (plants.getKickstart()->getPinNumber(i) >= 0) {
  1380. need_kickstart = true;
  1381. }
  1382. }
  1383. }
  1384. // start kickstart/valve as needed
  1385. for (int i = 0; i < plants.countPlants(); i++) {
  1386. if (selected_plants.isSet(i)) {
  1387. plants.startPlant(i, need_kickstart);
  1388. }
  1389. }
  1390. watering_started_full = (wl == Plants::full);
  1391. selected_time = MAX_AUTO_PLANT_RUNTIME;
  1392. start_time = millis();
  1393. if (need_kickstart) {
  1394. if (state == fillnwater_tank_run) {
  1395. switch_to(fillnwater_kickstart_run);
  1396. } else if (state == fullauto_tank_run) {
  1397. switch_to(fullauto_kickstart_run);
  1398. } else {
  1399. switch_to(fullauto_kickstart_purge_run);
  1400. }
  1401. } else {
  1402. if (state == fillnwater_tank_run) {
  1403. switch_to(fillnwater_plant_run);
  1404. } else if (state == fullauto_tank_run) {
  1405. switch_to(fullauto_plant_run);
  1406. } else {
  1407. switch_to(fullauto_plant_purge_run);
  1408. }
  1409. }
  1410. } else {
  1411. switch_to(auto_done);
  1412. }
  1413. } else if (wl == Plants::invalid) {
  1414. plants.abort();
  1415. error_condition = F("Invalid sensor state");
  1416. state = auto_mode_a;
  1417. switch_to(error);
  1418. }
  1419. }
  1420. // states that empty the tank
  1421. if ((state == auto_plant_run) || (state == fillnwater_plant_run)
  1422. || (state == fullauto_plant_run) || (state == fullauto_plant_overrun)
  1423. || (state == fullauto_plant_purge_run)
  1424. || (state == fullauto_plant_purge_overrun)) {
  1425. unsigned long runtime = millis() - start_time;
  1426. if ((runtime / 1000UL) >= selected_time) {
  1427. if (state == fullauto_plant_run) {
  1428. start_time = millis();
  1429. selected_time = OVERRUN_RUNTIME;
  1430. switch_to(fullauto_plant_overrun);
  1431. } else if (state == fullauto_plant_overrun) {
  1432. plants.abort();
  1433. stop_time = millis();
  1434. auto wl = plants.getWaterlevel();
  1435. if ((wl != Plants::full) && (wl != Plants::invalid)) {
  1436. // if the waterlevel is currently empty, we
  1437. // set a flag to record the time to fill
  1438. filling_started_empty = (wl == Plants::empty);
  1439. plants.openWaterInlet();
  1440. selected_id = plants.countPlants() + 1;
  1441. selected_time = PURGE_FILL_RUNTIME;
  1442. start_time = millis();
  1443. switch_to(fullauto_tank_purge_run);
  1444. } else if (wl == Plants::full) {
  1445. // check if kickstart is required for this
  1446. bool need_kickstart = false;
  1447. for (int i = 0; i < plants.countPlants(); i++) {
  1448. if (selected_plants.isSet(i)) {
  1449. if (plants.getKickstart()->getPinNumber(i) >= 0) {
  1450. need_kickstart = true;
  1451. }
  1452. }
  1453. }
  1454. // start kickstart/valve as needed
  1455. for (int i = 0; i < plants.countPlants(); i++) {
  1456. if (selected_plants.isSet(i)) {
  1457. plants.startPlant(i, need_kickstart);
  1458. }
  1459. }
  1460. // for recording the flowrate
  1461. watering_started_full = (wl == Plants::full);
  1462. selected_time = MAX_AUTO_PLANT_RUNTIME;
  1463. start_time = millis();
  1464. if (need_kickstart) {
  1465. switch_to(fullauto_kickstart_purge_run);
  1466. } else {
  1467. switch_to(fullauto_plant_purge_run);
  1468. }
  1469. } else if (wl == Plants::invalid) {
  1470. error_condition = F("Invalid sensor state");
  1471. state = auto_mode_a;
  1472. switch_to(error);
  1473. }
  1474. } else if (state == fullauto_plant_purge_run) {
  1475. start_time = millis();
  1476. selected_time = OVERRUN_RUNTIME;
  1477. switch_to(fullauto_plant_purge_overrun);
  1478. } else if (state == fullauto_plant_purge_overrun) {
  1479. plants.abort();
  1480. stop_time = millis();
  1481. switch_to(fullauto_done);
  1482. } else {
  1483. plants.abort();
  1484. stop_time = millis();
  1485. switch_to(auto_done);
  1486. }
  1487. } else if ((millis() - last_animation_time) >= 500) {
  1488. // update animation if needed
  1489. last_animation_time = millis();
  1490. switch_to(state);
  1491. }
  1492. if ((state == fullauto_plant_overrun) || (state == fullauto_plant_purge_overrun)) {
  1493. // overrun should not exit when sensor returns empty
  1494. return;
  1495. }
  1496. // check water level state
  1497. auto wl = plants.getWaterlevel();
  1498. if (wl == Plants::empty) {
  1499. if ((state == auto_plant_run) || (state == fillnwater_plant_run)) {
  1500. plants.abort();
  1501. stop_time = millis();
  1502. }
  1503. if ((state == auto_plant_run) || (state == fillnwater_plant_run)
  1504. || (state == fullauto_plant_run)) {
  1505. // if we started watering with a full tank
  1506. // and then finished watering when it was empty
  1507. // and we were only watering a single plant
  1508. // look at this as a "calibration run" and record
  1509. // the time it took to empty the tank
  1510. if (watering_started_full && (selected_plants.countSet() == 1)) {
  1511. unsigned long time_to_water = stop_time - start_time;
  1512. debug.print("Watering plant ");
  1513. debug.print(selected_plants.getFirstSet() + 1);
  1514. debug.print(" with the complete tank took ");
  1515. debug.print(String(time_to_water));
  1516. debug.println("ms");
  1517. #if defined(PLATFORM_ESP)
  1518. wifi_write_database(time_to_water, "calibrated_watering", selected_plants.getFirstSet() + 1);
  1519. #endif // PLATFORM_ESP
  1520. }
  1521. }
  1522. if ((state == auto_plant_run) || (state == fillnwater_plant_run)) {
  1523. switch_to(auto_done);
  1524. } else if (state == fullauto_plant_run) {
  1525. start_time = millis();
  1526. selected_time = OVERRUN_RUNTIME;
  1527. switch_to(fullauto_plant_overrun);
  1528. } else if (state == fullauto_plant_purge_run) {
  1529. start_time = millis();
  1530. selected_time = OVERRUN_RUNTIME;
  1531. switch_to(fullauto_plant_purge_overrun);
  1532. }
  1533. } else if (wl == Plants::invalid) {
  1534. plants.abort();
  1535. error_condition = F("Invalid sensor state");
  1536. state = auto_mode_a;
  1537. switch_to(error);
  1538. }
  1539. }
  1540. // timeout in user-input states
  1541. if ((state == menu_a) || (state == menu_b) || (state == menu_c)
  1542. || (state == automation_mode) || (state == auto_done)
  1543. || (state == auto_mode_a) || (state == auto_mode_b)
  1544. || (state == auto_fert_a) || (state == auto_fert_b)
  1545. || (state == auto_plant) || (state == fillnwater_plant)
  1546. || (state == menu_pumps) || (state == menu_pumps_time)
  1547. || (state == menu_pumps_go) || (state == menu_pumps_done)
  1548. || (state == menu_valves) || (state == menu_valves_time)
  1549. || (state == menu_valves_go) || (state == menu_valves_done)
  1550. || (state == menu_aux) || (state == menu_aux_time)
  1551. || (state == menu_aux_go) || (state == menu_aux_done)
  1552. || (state == fullauto_fert) || (state == fullauto_plant)
  1553. || (state == fullauto_done)) {
  1554. unsigned long runtime = millis() - into_state_time;
  1555. if (runtime >= BACK_TO_IDLE_TIMEOUT) {
  1556. debug.print("Idle timeout reached in state ");
  1557. debug.println(state_names[state]);
  1558. switch_to(init);
  1559. }
  1560. }
  1561. }
  1562. void Statemachine::switch_to(States s) {
  1563. old_state = state;
  1564. state = s;
  1565. into_state_time = millis();
  1566. if (old_state != state) {
  1567. // don't spam log with every animation state "change"
  1568. debug.print("switch_to ");
  1569. debug.print(state_names[old_state]);
  1570. debug.print(" --> ");
  1571. debug.println(state_names[state]);
  1572. menu_entered_digits = "";
  1573. }
  1574. if (s == init) {
  1575. String a = String(F("- Giess-o-mat V")) + FIRMWARE_VERSION + String(F(" -"));
  1576. #if (LOCK_COUNT > 0) && defined(DOOR_LOCK_PIN)
  1577. String b = String(F("PIN: ")) + menu_entered_digits;
  1578. print(a.c_str(),
  1579. "* or # to enter menu",
  1580. "Enter PIN for locks:",
  1581. b.c_str(),
  1582. 3);
  1583. #else
  1584. print(a.c_str(),
  1585. "Usage: Enter number",
  1586. "* Delete prev. digit",
  1587. "# Execute input num.",
  1588. -1);
  1589. #endif
  1590. } else if (s == door_select) {
  1591. String a = String(F("(Input 1 to ")) + String(LOCK_COUNT) + String(F(")"));
  1592. String b = String(F("Door: ")) + menu_entered_digits;
  1593. print("- Select Door Lock -",
  1594. "Leave empty if done!",
  1595. a.c_str(),
  1596. b.c_str(),
  1597. 3);
  1598. } else if (s == menu_a) {
  1599. print("----- Menu 1/3 -----",
  1600. "1: Manual Operation",
  1601. "2: Automation",
  1602. "#: Go to page 2/3...",
  1603. -1);
  1604. } else if (s == menu_b) {
  1605. print("----- Menu 2/3 -----",
  1606. "3: Fertilizer pumps",
  1607. "4: Outlet valves",
  1608. "#: Go to page 3/3...",
  1609. -1);
  1610. } else if (s == menu_c) {
  1611. print("----- Menu 3/3 -----",
  1612. "5: Aux. Outputs",
  1613. #if (LOCK_COUNT > 0) && !defined(DOOR_LOCK_PIN)
  1614. "6: Door Locks",
  1615. #else
  1616. "",
  1617. #endif
  1618. "#: Go to page 1/3...",
  1619. -1);
  1620. } else if (state == automation_mode) {
  1621. // TODO
  1622. print("---- Automation ----",
  1623. "TODO NOT IMPLEMENTED",
  1624. "TODO NOT IMPLEMENTED",
  1625. "TODO NOT IMPLEMENTED",
  1626. -1);
  1627. } else if (s == auto_mode_a) {
  1628. print("---- Manual 1/3 ----",
  1629. "1: Full-Auto Mode",
  1630. "2: Add Fertilizer",
  1631. "#: Go to page 2/3...",
  1632. -1);
  1633. } else if (s == auto_mode_b) {
  1634. print("---- Manual 2/3 ----",
  1635. "3: Fill 'n' Water",
  1636. "4: Fill Reservoir",
  1637. "#: Go to page 3/3...",
  1638. -1);
  1639. } else if (s == auto_mode_c) {
  1640. print("---- Manual 3/3 ----",
  1641. "5: Water a plant",
  1642. "",
  1643. "#: Go to page 1/3...",
  1644. -1);
  1645. } else if (s == auto_fert_a) {
  1646. print("-- Fertilizer 1/2 --",
  1647. "1: Vegetation Phase",
  1648. "2: Bloom Phase",
  1649. "#: Go to page 2/2...",
  1650. -1);
  1651. } else if (s == auto_fert_b) {
  1652. print("-- Fertilizer 2/2 --",
  1653. "3: Special Fert.",
  1654. "4: Run Stirrer",
  1655. "#: Go to page 1/2...",
  1656. -1);
  1657. } else if ((s == auto_fert_run) || (s == fullauto_fert_run)) {
  1658. unsigned long runtime = millis() - start_time;
  1659. String a = String(F("Time: ")) + String(runtime / 1000UL) + String(F("s / ")) + String(selected_time) + String('s');
  1660. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  1661. String b;
  1662. for (unsigned long i = 0; i < anim; i++) {
  1663. b += '#';
  1664. }
  1665. print("---- Dispensing ----",
  1666. a.c_str(),
  1667. b.c_str(),
  1668. "Hit any key to stop!",
  1669. -1);
  1670. } else if ((s == auto_stirr_run) || (s == fullauto_stirr_run)) {
  1671. unsigned long runtime = millis() - start_time;
  1672. String a = String(F("Time: ")) + String(runtime / 1000UL) + String(F("s / ")) + String(selected_time) + String('s');
  1673. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  1674. String b;
  1675. for (unsigned long i = 0; i < anim; i++) {
  1676. b += '#';
  1677. }
  1678. print("----- Stirring -----",
  1679. a.c_str(),
  1680. b.c_str(),
  1681. "Hit any key to stop!",
  1682. -1);
  1683. } else if ((s == auto_tank_run) || (s == fillnwater_tank_run) || (s == fullauto_tank_run) || (s == fullauto_tank_purge_run)) {
  1684. unsigned long runtime = millis() - start_time;
  1685. String a = String(F("Time: ")) + String(runtime / 1000UL) + String(F("s / ")) + String(selected_time) + String('s');
  1686. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  1687. String b;
  1688. for (unsigned long i = 0; i < anim; i++) {
  1689. b += '#';
  1690. }
  1691. print("--- Filling Tank ---",
  1692. a.c_str(),
  1693. b.c_str(),
  1694. "Hit any key to stop!",
  1695. -1);
  1696. } else if ((s == auto_plant) || (s == fillnwater_plant) || (s == fullauto_plant)) {
  1697. String a = String(F("(Input 1 to ")) + String(plants.countPlants()) + String(F(")"));
  1698. String b = String(F("Plant: ")) + menu_entered_digits;
  1699. print("--- Select Plant ---",
  1700. "Leave empty if done!",
  1701. a.c_str(),
  1702. b.c_str(),
  1703. 3);
  1704. } else if (s == fullauto_fert) {
  1705. String a = String(F("(Input 1 to ")) + String(plants.countFertilizers()) + String(F(")"));
  1706. String b = String(F("Fert.: ")) + menu_entered_digits;
  1707. print("--- Select Fert. ---",
  1708. "Leave empty if done!",
  1709. a.c_str(),
  1710. b.c_str(),
  1711. 3);
  1712. } else if ((s == auto_plant_kickstart_run) || (s == fillnwater_kickstart_run) || (s == fullauto_kickstart_run) || (s == fullauto_kickstart_purge_run)) {
  1713. unsigned long runtime = millis() - start_time;
  1714. String a = String(F("Time: ")) + String(runtime / 1000UL) + String(F("s / ")) + String(KICKSTART_RUNTIME) + String('s');
  1715. unsigned long anim = runtime * 20UL / (KICKSTART_RUNTIME * 1000UL);
  1716. String b;
  1717. for (unsigned long i = 0; i < anim; i++) {
  1718. b += '#';
  1719. }
  1720. print("---- Kick-Start ----",
  1721. a.c_str(),
  1722. b.c_str(),
  1723. "Hit any key to stop!",
  1724. -1);
  1725. } else if ((s == auto_plant_run) || (s == fillnwater_plant_run) || (s == fullauto_plant_run) || (s == fullauto_plant_purge_run)) {
  1726. unsigned long runtime = millis() - start_time;
  1727. String a = String(F("Time: ")) + String(runtime / 1000UL) + String(F("s / ")) + String(selected_time) + String('s');
  1728. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  1729. String b;
  1730. for (unsigned long i = 0; i < anim; i++) {
  1731. b += '#';
  1732. }
  1733. print("----- Watering -----",
  1734. a.c_str(),
  1735. b.c_str(),
  1736. "Hit any key to stop!",
  1737. -1);
  1738. } else if ((s == fullauto_plant_overrun) || (s == fullauto_plant_purge_overrun)) {
  1739. unsigned long runtime = millis() - start_time;
  1740. String a = String(F("Time: ")) + String(runtime / 1000UL) + String(F("s / ")) + String(selected_time) + String('s');
  1741. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  1742. String b;
  1743. for (unsigned long i = 0; i < anim; i++) {
  1744. b += '#';
  1745. }
  1746. print("-- Emptying lines --",
  1747. a.c_str(),
  1748. b.c_str(),
  1749. "Hit any key to stop!",
  1750. -1);
  1751. } else if ((s == auto_done) || (s == fullauto_done)) {
  1752. String a = String(F("after ")) + String((stop_time - start_time) / 1000UL) + String(F("s."));
  1753. print("------- Done -------",
  1754. "Dispensing finished",
  1755. a.c_str(),
  1756. "Hit any key for menu",
  1757. -1);
  1758. #if defined(PLATFORM_ESP)
  1759. unsigned long runtime = stop_time - start_time;
  1760. if ((old_state == auto_plant_run) || (old_state == fillnwater_plant_run)) {
  1761. for (int i = 0; i < plants.countPlants(); i++) {
  1762. if (selected_plants.isSet(i)) {
  1763. wifi_write_database(runtime / 1000, "plant", i + 1);
  1764. }
  1765. }
  1766. } else if (old_state == auto_fert_run) {
  1767. wifi_write_database(runtime / 1000, "fertilizer", selected_id);
  1768. }
  1769. #endif // PLATFORM_ESP
  1770. } else if (s == menu_pumps) {
  1771. String a = String(F("(Input 1 to ")) + String(plants.countFertilizers()) + String(F(")"));
  1772. String b = String(F("Pump: ")) + menu_entered_digits;
  1773. print("------- Pump -------",
  1774. "Please select pump",
  1775. a.c_str(),
  1776. b.c_str(),
  1777. 3);
  1778. } else if (s == menu_pumps_time) {
  1779. String header = String(F("------ Pump ")) + String(selected_id) + String(F(" ------"));
  1780. String b = String(F("Runtime: ")) + menu_entered_digits;
  1781. print(header.c_str(),
  1782. "Please set runtime",
  1783. "(Input in seconds)",
  1784. b.c_str(),
  1785. 3);
  1786. } else if (s == menu_pumps_go) {
  1787. String a = String(F("Pump No. ")) + String(selected_id);
  1788. String b = String(F("Runtime ")) + String(selected_time) + String('s');
  1789. print("----- Confirm? -----",
  1790. a.c_str(),
  1791. b.c_str(),
  1792. " # Confirm",
  1793. -1);
  1794. } else if (s == menu_pumps_run) {
  1795. unsigned long runtime = millis() - start_time;
  1796. String a = String(F("Time: ")) + String(runtime / 1000UL) + String(F("s / ")) + String(selected_time) + String('s');
  1797. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  1798. String b;
  1799. for (unsigned long i = 0; i < anim; i++) {
  1800. b += '#';
  1801. }
  1802. print("---- Dispensing ----",
  1803. a.c_str(),
  1804. b.c_str(),
  1805. "Hit any key to stop!",
  1806. -1);
  1807. } else if (s == menu_pumps_done) {
  1808. String a = String(F("after ")) + String((stop_time - start_time) / 1000UL) + String(F("s."));
  1809. print("------- Done -------",
  1810. "Dispensing finished",
  1811. a.c_str(),
  1812. "Hit any key for menu",
  1813. -1);
  1814. #if defined(PLATFORM_ESP)
  1815. unsigned long runtime = stop_time - start_time;
  1816. wifi_write_database(runtime / 1000, "fertilizer", selected_id);
  1817. #endif // PLATFORM_ESP
  1818. } else if (s == menu_valves) {
  1819. String a = String(F("(Input 1 to ")) + String(plants.countPlants() + 1) + String(F(")"));
  1820. String b = String(F("Valve: ")) + menu_entered_digits;
  1821. print("------ Valves ------",
  1822. "Please select valve",
  1823. a.c_str(),
  1824. b.c_str(),
  1825. 3);
  1826. } else if (s == menu_valves_time) {
  1827. String header = String(F("----- Valve ")) + String(selected_id) + String(F(" -----"));
  1828. String b = String(F("Runtime: ")) + menu_entered_digits;
  1829. print(header.c_str(),
  1830. "Please set runtime",
  1831. "(Input in seconds)",
  1832. b.c_str(),
  1833. 3);
  1834. } else if (s == menu_valves_go) {
  1835. String a = String(F("Valve No. ")) + String(selected_id);
  1836. String b = String(F("Runtime ")) + String(selected_time) + String('s');
  1837. print("----- Confirm? -----",
  1838. a.c_str(),
  1839. b.c_str(),
  1840. " # Confirm",
  1841. -1);
  1842. } else if (s == menu_valves_run) {
  1843. unsigned long runtime = millis() - start_time;
  1844. String a = String(F("Time: ")) + String(runtime / 1000UL) + String(F("s / ")) + String(selected_time) + String('s');
  1845. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  1846. String b;
  1847. for (unsigned long i = 0; i <= anim; i++) {
  1848. b += '#';
  1849. }
  1850. print("---- Dispensing ----",
  1851. a.c_str(),
  1852. b.c_str(),
  1853. "Hit any key to stop!",
  1854. -1);
  1855. } else if (s == menu_valves_done) {
  1856. String a = String(F("after ")) + String((stop_time - start_time) / 1000UL) + String(F("s."));
  1857. print("------- Done -------",
  1858. "Dispensing finished",
  1859. a.c_str(),
  1860. "Hit any key for menu",
  1861. -1);
  1862. #if defined(PLATFORM_ESP)
  1863. unsigned long runtime = stop_time - start_time;
  1864. if (selected_id <= plants.countPlants()) {
  1865. wifi_write_database(runtime / 1000, "plant", selected_id);
  1866. }
  1867. #endif // PLATFORM_ESP
  1868. } else if (s == menu_aux) {
  1869. String a = String(F("(Input 1 to ")) + String(plants.countAux()) + String(F(")"));
  1870. String b = String(F("Aux.: ")) + menu_entered_digits;
  1871. print("------- Aux. -------",
  1872. "Please select aux.",
  1873. a.c_str(),
  1874. b.c_str(),
  1875. 3);
  1876. } else if (s == menu_aux_time) {
  1877. String header = String(F("------ Aux ")) + String(selected_id) + String(F(" ------"));
  1878. String b = String(F("Runtime: ")) + menu_entered_digits;
  1879. print(header.c_str(),
  1880. "Please set runtime",
  1881. "(Input in seconds)",
  1882. b.c_str(),
  1883. 3);
  1884. } else if (s == menu_aux_go) {
  1885. String a = String(F("Aux No. ")) + String(selected_id);
  1886. String b = String(F("Runtime ")) + String(selected_time) + String('s');
  1887. print("----- Confirm? -----",
  1888. a.c_str(),
  1889. b.c_str(),
  1890. " # Confirm",
  1891. -1);
  1892. } else if (s == menu_aux_run) {
  1893. unsigned long runtime = millis() - start_time;
  1894. String a = String(F("Time: ")) + String(runtime / 1000UL) + String(F("s / ")) + String(selected_time) + String('s');
  1895. unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
  1896. String b;
  1897. for (unsigned long i = 0; i <= anim; i++) {
  1898. b += '#';
  1899. }
  1900. print("----- Stirring -----",
  1901. a.c_str(),
  1902. b.c_str(),
  1903. "Hit any key to stop!",
  1904. -1);
  1905. } else if (s == menu_aux_done) {
  1906. String a = String(F("after ")) + String((stop_time - start_time) / 1000UL) + String(F("s."));
  1907. print("------- Done -------",
  1908. "Aux. run finished",
  1909. a.c_str(),
  1910. "Hit any key for menu",
  1911. -1);
  1912. } else if (s == error) {
  1913. print("------ Error! ------",
  1914. "There is a problem:",
  1915. error_condition.c_str(),
  1916. " Press any key...",
  1917. -1);
  1918. } else {
  1919. debug.print("Invalid state ");
  1920. debug.println(s);
  1921. }
  1922. }
  1923. #endif