controller.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. /*
  2. * Moistcontrol - Controller state machine
  3. *
  4. * Copyright (c) 2013 Michael Buesch <m@bues.ch>
  5. *
  6. * This program 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 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program 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 along
  17. * with this program; if not, write to the Free Software Foundation, Inc.,
  18. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. */
  20. #include "controller.h"
  21. #include "sensor.h"
  22. #include "util.h"
  23. #include "main.h"
  24. #include "rv3029.h"
  25. #include "ioext.h"
  26. #include "log.h"
  27. #include <string.h>
  28. #include <avr/eeprom.h>
  29. /* Controller interval time, in seconds.
  30. * This is the time the controller waits between measurements.
  31. */
  32. #define CTRL_INTERVAL_SEC 60
  33. /* First wait time, in seconds.
  34. * This is the time the controller waits before doing the first
  35. * measurement after a controller reset.
  36. */
  37. #define FIRST_CTRL_INTERVAL_SEC 10
  38. /* The time a valve is held "opened" when watering.
  39. * In milliseconds.
  40. */
  41. #define VALVE_OPEN_MS 3000
  42. /* The time a value is held "closed" when watering before doing
  43. * the next measurement. In milliseconds.
  44. */
  45. #define VALVE_CLOSE_MS 30000
  46. /* Flowerpot controller context data structure. */
  47. struct flowerpot {
  48. /* The ID-number of this pot. */
  49. uint8_t nr;
  50. /* The current state of the controller state machine
  51. * for this pot.
  52. */
  53. struct flowerpot_state state;
  54. /* Timestamp for the next measurement. */
  55. jiffies_t next_measurement;
  56. /* Timer variable for the VALVE_OPEN_MS and
  57. * VALVE_CLOSE_MS times.
  58. */
  59. jiffies_t valve_timer;
  60. /* Enable-state of manual-mode for this pot's valve.
  61. * Manual mode is enabled, if this bit is 1.
  62. */
  63. bool valve_manual_en;
  64. /* Manual-mode state for this pot's valve.
  65. * The valve is force opened, if this bit is 1
  66. * and manual mode is enabled.
  67. * The valve is force closed, if this bit is 0
  68. * and manual mode is enabled.
  69. */
  70. bool valve_manual_state;
  71. /* Automatic-mode state for this pot's valve.
  72. * The valve is opened, if this bit is 1
  73. * and manual mode is disabled.
  74. * The valve is closed, if this bit is 0
  75. * and manual mode is disabled.
  76. */
  77. bool valve_auto_state;
  78. };
  79. /* Controller context data structure. */
  80. struct controller {
  81. /* The active controller configuration.
  82. * This is a RAM-copy of the EEPROM contents.
  83. */
  84. struct controller_config config;
  85. /* The instances of the flowerpot contexts. */
  86. struct flowerpot pots[MAX_NR_FLOWERPOTS];
  87. /* Counter variable for the flowerpot cycle. */
  88. uint8_t current_pot;
  89. /* EEPROM-update flag.
  90. * If this bit is set, an EEPROM update is pending.
  91. * The EEPROM update will be performed as soon as
  92. * the timer has expired.
  93. */
  94. bool eeprom_update_required;
  95. /* The EEPROM-update timer. */
  96. jiffies_t eeprom_update_time;
  97. };
  98. /* Instance of the controller context. */
  99. static struct controller cont;
  100. /* Initialization helper macro for pot config. */
  101. #define POT_DEFAULT_CONFIG \
  102. { \
  103. .flags = 0, \
  104. .min_threshold = 85, \
  105. .max_threshold = 170, \
  106. .active_range = { \
  107. .from = 0, \
  108. .to = (time_of_day_t)(long)-1, \
  109. }, \
  110. .dow_on_mask = 0x7F, \
  111. }
  112. /* The EEPROM memory, for storage of configuration values. */
  113. static struct controller_config EEMEM eeprom_cont_config = {
  114. .pots[0] = POT_DEFAULT_CONFIG,
  115. .pots[1] = POT_DEFAULT_CONFIG,
  116. .pots[2] = POT_DEFAULT_CONFIG,
  117. .pots[3] = POT_DEFAULT_CONFIG,
  118. .pots[4] = POT_DEFAULT_CONFIG,
  119. .pots[5] = POT_DEFAULT_CONFIG,
  120. .global = {
  121. .flags = CONTR_FLG_ENABLE,
  122. .sensor_lowest_value = 0,
  123. .sensor_highest_value = SENSOR_MAX,
  124. },
  125. };
  126. /* Get a pointer to the configuration structure for a pot.
  127. * pot: A pointer to the flowerpot.
  128. */
  129. static inline struct flowerpot_config * pot_config(const struct flowerpot *pot)
  130. {
  131. return &cont.config.pots[pot->nr];
  132. }
  133. /* Emit a log message, if logging is enabled.
  134. * pot: A pointer to the flowerpot.
  135. * log_class: The logging class to use. May be LOG_INFO or LOG_ERROR.
  136. * log_code: The info or error code.
  137. * log_data: Additional user-data for this log message.
  138. */
  139. static void pot_info(struct flowerpot *pot,
  140. uint8_t log_class, uint8_t log_code, uint8_t log_data)
  141. {
  142. struct log_item log;
  143. if (!(pot_config(pot)->flags & POT_FLG_LOG)) {
  144. /* Logging is disabled. Do not emit the message. */
  145. return;
  146. }
  147. /* Construct the log data structure. */
  148. log_init(&log, log_class);
  149. log.code = log_code;
  150. log.data = log_data;
  151. /* Append the log message to the log queue. */
  152. log_append(&log);
  153. }
  154. /* Emit a log message, if verbose logging is enabled.
  155. * pot: A pointer to the flowerpot.
  156. * log_class: The logging class to use. May be LOG_INFO or LOG_ERROR.
  157. * log_code: The info or error code.
  158. * log_data: Additional user-data for this log message.
  159. */
  160. static void pot_info_verbose(struct flowerpot *pot,
  161. uint8_t log_class, uint8_t log_code, uint8_t log_data)
  162. {
  163. if (pot_config(pot)->flags & POT_FLG_LOGVERBOSE)
  164. pot_info(pot, log_class, log_code, log_data);
  165. }
  166. /* Switch the controller state machine into another state.
  167. * pot: A pointer to the flowerpot.
  168. * new_state: The new state to switch to.
  169. */
  170. static void pot_state_enter(struct flowerpot *pot,
  171. enum flowerpot_state_id new_state)
  172. {
  173. uint8_t data;
  174. /* Switch state, if new_state is different from the current state. */
  175. if (pot->state.state_id != new_state) {
  176. pot->state.state_id = new_state;
  177. /* Emit a verbose log message for this switch operation.
  178. * The log data holds the pot number in the lower 4 bits
  179. * and the new state number in the upper 4 bits.
  180. */
  181. data = ((uint8_t)new_state << 4) | (pot->nr & 0xF);
  182. pot_info_verbose(pot, LOG_INFO, LOG_INFO_CONTSTATCHG, data);
  183. }
  184. }
  185. /* Scale the raw sensor ADC value into the fixed 0-255 moisture range.
  186. * res: Pointer to the sensor result (ADC value).
  187. * Returns the 8-bit scaled value.
  188. */
  189. static uint8_t scale_sensor_val(const struct sensor_result *res)
  190. {
  191. uint16_t raw_value = res->value;
  192. uint16_t raw_range;
  193. uint16_t raw_lowest, raw_highest;
  194. uint8_t scaled_value;
  195. /* Clamp the raw sensor value between the lowest and highest
  196. * possible values. This aides minimal range overshoots.
  197. * Also subtract the lower boundary to adjust the range to zero.
  198. */
  199. raw_lowest = cont.config.global.sensor_lowest_value;
  200. raw_highest = cont.config.global.sensor_highest_value;
  201. raw_value = clamp(raw_value, raw_lowest, raw_highest);
  202. raw_value -= raw_lowest;
  203. raw_range = raw_highest - raw_lowest;
  204. /* Scale the value with the formula:
  205. * scaled_value = scale_max / raw_range * raw_value
  206. * where scale_max is UINT8_MAX (= 0xFF).
  207. * Honor division by zero and precision loss on division.
  208. * Round to the nearest integer.
  209. */
  210. if (raw_range) {
  211. scaled_value = div_round((uint32_t)UINT8_MAX * raw_value,
  212. (uint32_t)raw_range);
  213. } else
  214. scaled_value = 0;
  215. return scaled_value;
  216. }
  217. /* Get the output extender bit-number for a valve.
  218. * nr: The valve number to get the extender-bit-number for.
  219. * Returns the output extender bit value.
  220. */
  221. static uint8_t valvenr_to_bitnr(uint8_t nr)
  222. {
  223. switch (nr) {
  224. case 0:
  225. return EXTOUT_VALVE0;
  226. case 1:
  227. return EXTOUT_VALVE1;
  228. case 2:
  229. return EXTOUT_VALVE2;
  230. case 3:
  231. return EXTOUT_VALVE3;
  232. case 4:
  233. return EXTOUT_VALVE4;
  234. case 5:
  235. return EXTOUT_VALVE5;
  236. }
  237. return 0;
  238. }
  239. /* Write the current valve state out to the valve hardware.
  240. * pot: A pointer to the flowerpot.
  241. */
  242. static void valve_state_commit(struct flowerpot *pot)
  243. {
  244. uint8_t bitnr = valvenr_to_bitnr(pot->nr);
  245. bool state;
  246. if (pot->valve_manual_en)
  247. state = pot->valve_manual_state;
  248. else
  249. state = pot->valve_auto_state;
  250. /* Invert the state. The hardware will invert it again. */
  251. state = !state;
  252. /* And write it to the hardware. */
  253. ioext_write_bit(bitnr, state);
  254. ioext_commit();
  255. }
  256. /* Set the automatic-state of a valve to "closed"
  257. * and write the state to the hardware.
  258. * pot: A pointer to the flowerpot.
  259. */
  260. static void valve_close(struct flowerpot *pot)
  261. {
  262. pot->valve_auto_state = 0;
  263. valve_state_commit(pot);
  264. }
  265. /* Set the automatic-state of a valve to "opened"
  266. * and write the state to the hardware.
  267. * Also start the valve-open-timer and switch the state machine
  268. * into the "waiting-for-valve" state.
  269. * pot: A pointer to the flowerpot.
  270. */
  271. static void valve_open(struct flowerpot *pot)
  272. {
  273. pot->valve_auto_state = 1;
  274. valve_state_commit(pot);
  275. /* Set state machine to waiting-for-valve. */
  276. pot->valve_timer = jiffies_get() + msec_to_jiffies(VALVE_OPEN_MS);
  277. pot_state_enter(pot, POT_WAITING_FOR_VALVE);
  278. }
  279. /* Start a sensor measurement and switch the state machine
  280. * into the "measuring" state.
  281. * pot: A pointer to the flowerpot.
  282. */
  283. static void pot_start_measurement(struct flowerpot *pot)
  284. {
  285. sensor_start(pot->nr);
  286. pot_state_enter(pot, POT_MEASURING);
  287. }
  288. /* Switch the state machine into the "idle" state.
  289. * Also schedule the next measurement.
  290. * pot: A pointer to the flowerpot.
  291. */
  292. static void pot_go_idle(struct flowerpot *pot)
  293. {
  294. pot->next_measurement = jiffies_get() + sec_to_jiffies(CTRL_INTERVAL_SEC);
  295. pot_state_enter(pot, POT_IDLE);
  296. }
  297. /* Start watering.
  298. * This will set the "watering" state and open the valve.
  299. * pot: A pointer to the flowerpot.
  300. */
  301. static void pot_start_watering(struct flowerpot *pot)
  302. {
  303. /* Emit a "watering started" log message.
  304. * The lower 4 bits of the log data is the pot number
  305. * and the 7th bit is the "watering active" bit.
  306. */
  307. pot_info(pot, LOG_INFO, LOG_INFO_WATERINGCHG,
  308. (pot->nr & 0x0F) | 0x80);
  309. /* Go into watering state and open the valve. */
  310. pot->state.is_watering = 1;
  311. valve_open(pot);
  312. }
  313. /* Stop watering.
  314. * This will reset the "watering" state and close the valve.
  315. * Additionally it will reset the controller state machine to "idle".
  316. * pot: A pointer to the flowerpot.
  317. */
  318. static void pot_stop_watering(struct flowerpot *pot)
  319. {
  320. if (pot->state.is_watering) {
  321. /* Emit a "watering stopped" log message.
  322. * The lower 4 bits of the log data is the pot number
  323. * and the 7th bit is the "watering active" bit.
  324. */
  325. pot_info(pot, LOG_INFO, LOG_INFO_WATERINGCHG,
  326. pot->nr & 0x0F);
  327. /* Go out of watering state, close the valve and
  328. * set the state machine to "idle"
  329. */
  330. pot->state.is_watering = 0;
  331. }
  332. valve_close(pot);
  333. pot_go_idle(pot);
  334. }
  335. /* Reset the state machine on one pot.
  336. * pot: A pointer to the flowerpot.
  337. */
  338. static void pot_reset(struct flowerpot *pot)
  339. {
  340. if (pot->state.state_id == POT_MEASURING) {
  341. /* We are currently measuring on this
  342. * pot. Cancel the sensor measurement.
  343. */
  344. sensor_cancel();
  345. }
  346. /* Reset all state values. */
  347. pot->state.is_watering = 0;
  348. pot->next_measurement = jiffies_get() + sec_to_jiffies(FIRST_CTRL_INTERVAL_SEC);
  349. pot_state_enter(pot, POT_IDLE);
  350. pot->valve_manual_en = 0;
  351. pot->valve_manual_state = 0;
  352. /* Make sure the valve is closed. */
  353. valve_close(pot);
  354. }
  355. /* The pot controller state machine routine.
  356. * pot: A pointer to the flowerpot.
  357. */
  358. static void handle_pot(struct flowerpot *pot)
  359. {
  360. struct sensor_result result;
  361. uint8_t sensor_val;
  362. jiffies_t now;
  363. bool ok;
  364. struct rtc_time rtc;
  365. time_of_day_t tod;
  366. uint8_t dow_mask;
  367. const struct flowerpot_config *config = pot_config(pot);
  368. now = jiffies_get();
  369. switch (pot->state.state_id) {
  370. case POT_IDLE:
  371. /* Idle: We are not doing anything, yet. */
  372. if (!(config->flags & POT_FLG_ENABLED)) {
  373. /* This pot is disabled. Don't do anything. */
  374. break;
  375. }
  376. /* Get the current RTC time. */
  377. rv3029_get_time(&rtc);
  378. /* Create a day-of-week mask for today. */
  379. dow_mask = BITMASK8(rtc.day_of_week);
  380. if (!(config->dow_on_mask & dow_mask)) {
  381. /* This pot is disabled on today's weekday. */
  382. break;
  383. }
  384. /* Check if we are in the active-time-range. */
  385. tod = rtc_get_time_of_day(&rtc);
  386. if (time_of_day_before(tod, config->active_range.from) ||
  387. time_of_day_after(tod, config->active_range.to)) {
  388. /* Current time is not in the active range.
  389. * Don't run.
  390. */
  391. break;
  392. }
  393. /* Check, if the next measurement is pending. */
  394. if (time_before(now, pot->next_measurement)) {
  395. /* No. Don't run, yet. */
  396. break;
  397. }
  398. /* It's time to start a new measurement. */
  399. pot_state_enter(pot, POT_START_MEASUREMENT);
  400. break;
  401. case POT_START_MEASUREMENT:
  402. /* Start the measurement as soon as the sensors go idle. */
  403. if (!sensors_idle()) {
  404. /* Another sensor measurement is still running.
  405. * Cannot start this measurement, yet.
  406. */
  407. break;
  408. }
  409. /* Start a measurement on this pot, now. */
  410. pot_start_measurement(pot);
  411. break;
  412. case POT_MEASURING:
  413. /* Poll the sensor state. */
  414. ok = sensor_poll(&result);
  415. if (!ok) {
  416. /* The measurement did not finish, yet. */
  417. break;
  418. }
  419. /* Check if verbose logging is requested for this pot
  420. * and send the raw measurement result.
  421. */
  422. if (config->flags & POT_FLG_LOGVERBOSE) {
  423. struct log_item log;
  424. /* Get the current RTC time. */
  425. rv3029_get_time(&rtc);
  426. /* Fill out the log data structure
  427. * with the current timestamp and sensor values
  428. * and queue it in the log subsystem.
  429. */
  430. log_init(&log, LOG_SENSOR_DATA);
  431. log.sensor_data = LOG_SENSOR_DATA(result.nr,
  432. result.value);
  433. log_append(&log);
  434. }
  435. /* Scale the raw sensor value to the 8-bit
  436. * data type of the controller logics.
  437. */
  438. sensor_val = scale_sensor_val(&result);
  439. pot->state.last_measured_raw_value = result.value;
  440. pot->state.last_measured_value = sensor_val;
  441. /* Sensor value sanity check.
  442. * Too low and too big values are rejected. These values
  443. * indicate a short circuit or cable break.
  444. */
  445. if (result.value < 16 || result.value > (SENSOR_MAX - 16)) {
  446. pot_info(pot, LOG_ERROR, LOG_ERR_SENSOR, pot->nr);
  447. /* Force-stop watering and bail to idle state. */
  448. pot_stop_watering(pot);
  449. break;
  450. }
  451. if (pot->state.is_watering) {
  452. /* We are watering. Check if we reached the upper threshold.
  453. * If so, stop watering.
  454. * If not, go on watering.
  455. */
  456. if (sensor_val >= config->max_threshold)
  457. pot_stop_watering(pot);
  458. else
  459. valve_open(pot);
  460. } else {
  461. /* We are not watering, yet. Check if we dropped below
  462. * the lower threshold.
  463. * If so, start watering.
  464. * If not, don't do anything and go idle.
  465. */
  466. if (sensor_val < config->min_threshold)
  467. pot_start_watering(pot);
  468. else
  469. pot_go_idle(pot);
  470. }
  471. break;
  472. case POT_WAITING_FOR_VALVE:
  473. /* Wait for the valve open/close cycle to finish. */
  474. if (time_before(now, pot->valve_timer)) {
  475. /* Open or close time not expired, yet. */
  476. break;
  477. }
  478. if (pot->valve_auto_state) {
  479. /* Valve is currently opened.
  480. * Close it and start the close timer.
  481. */
  482. valve_close(pot);
  483. pot->valve_timer = now + msec_to_jiffies(VALVE_CLOSE_MS);
  484. } else {
  485. /* Close timer expired.
  486. * Perform a new measurement, now. */
  487. pot_state_enter(pot, POT_START_MEASUREMENT);
  488. }
  489. break;
  490. }
  491. }
  492. /* Completely reset all controllers.
  493. * This resets all state machines and the corresponding hardware.
  494. */
  495. static void controller_reset(void)
  496. {
  497. uint8_t i;
  498. for (i = 0; i < ARRAY_SIZE(cont.pots); i++)
  499. pot_reset(&cont.pots[i]);
  500. cont.current_pot = 0;
  501. }
  502. /* Get the controller configuration.
  503. * Copies the current config into "dest".
  504. * dest: Pointer to the destination buffer.
  505. */
  506. void controller_get_config(struct controller_config *dest)
  507. {
  508. *dest = cont.config;
  509. }
  510. /* Set a new controller configuration.
  511. * Copies the "new_config" into the current config
  512. * and schedules an EEPROM update.
  513. * The affected controllers are reset, if the configuration changed.
  514. * new_config: Pointer to the new configuration.
  515. */
  516. void controller_update_config(const struct controller_config *new_config)
  517. {
  518. struct controller_config *active;
  519. uint8_t i;
  520. /* Get pointer to the currently active configuration. */
  521. active = &cont.config;
  522. /* Check which part of the config changed, if any. */
  523. if (memcmp(&new_config->global, &active->global,
  524. sizeof(new_config->global)) == 0) {
  525. /* Global config did not change.
  526. * Check if a pot changed.
  527. */
  528. for (i = 0; i < MAX_NR_FLOWERPOTS; i++) {
  529. if (memcmp(&new_config->pots[i], &active->pots[i],
  530. sizeof(new_config->pots[i])) != 0) {
  531. /* This pot changed.
  532. * Reset the pot's state machine.
  533. */
  534. pot_reset(&cont.pots[i]);
  535. }
  536. }
  537. } else {
  538. /* Global config differs.
  539. * Reset the complete controller state machine (all pots).
  540. */
  541. controller_reset();
  542. }
  543. /* Copy the new configuration to the active config in RAM. */
  544. cont.config = *new_config;
  545. /* Schedule an EEPROM update. */
  546. cont.eeprom_update_time = jiffies_get() + msec_to_jiffies(3000);
  547. cont.eeprom_update_required = 1;
  548. }
  549. /* Get the state information for a given pot.
  550. * pot_number: The number of the pot to get the state for.
  551. * state: A pointer to the buffer the state will be copied into.
  552. */
  553. void controller_get_pot_state(uint8_t pot_number,
  554. struct flowerpot_state *state)
  555. {
  556. if (pot_number < ARRAY_SIZE(cont.pots))
  557. *state = cont.pots[pot_number].state;
  558. }
  559. /* Set the "manual mode" control bits.
  560. * force_stop_watering_mask: A bitmask of pots to force-stop watering on.
  561. * valve_manual_mask: A bitmask of pots to enable manual valve control on.
  562. * valve_manual_state: A bitmask of manual-mode valve states.
  563. */
  564. void controller_manual_mode(uint8_t force_stop_watering_mask,
  565. uint8_t valve_manual_mask,
  566. uint8_t valve_manual_state)
  567. {
  568. struct flowerpot *pot;
  569. uint8_t i, mask;
  570. for (i = 0, mask = 1; i < MAX_NR_FLOWERPOTS; i++, mask <<= 1) {
  571. pot = &cont.pots[i];
  572. if ((force_stop_watering_mask & mask) &&
  573. pot->state.is_watering)
  574. pot_stop_watering(pot);
  575. pot->valve_manual_en = !!(valve_manual_mask & mask);
  576. pot->valve_manual_state = !!(valve_manual_state & mask);
  577. valve_state_commit(pot);
  578. }
  579. }
  580. /* The main controller routine. */
  581. void controller_work(void)
  582. {
  583. if (cont.eeprom_update_required &&
  584. time_after(jiffies_get(), cont.eeprom_update_time)) {
  585. cont.eeprom_update_required = 0;
  586. /* An EEPROM write was scheduled.
  587. * Update the EEPROM contents.
  588. * This only updates the bytes that changed. (reduces wearout)
  589. */
  590. eeprom_update_block_wdtsafe(&cont.config, &eeprom_cont_config,
  591. sizeof(cont.config));
  592. }
  593. if (cont.config.global.flags & CONTR_FLG_ENABLE) {
  594. /* The controller is enabled globally.
  595. * Run the pot state machines. */
  596. handle_pot(&cont.pots[cont.current_pot]);
  597. cont.current_pot++;
  598. if (cont.current_pot >= ARRAY_SIZE(cont.pots))
  599. cont.current_pot = 0;
  600. }
  601. }
  602. /* Initialization of the controller data structures and hardware. */
  603. void controller_init(void)
  604. {
  605. struct flowerpot *pot;
  606. uint8_t i;
  607. /* Initialize the output extender hardware (shift register).
  608. * All valves are connected through this extender.
  609. */
  610. ioext_init(1);
  611. /* Read the configuration from EEPROM. */
  612. memset(&cont, 0, sizeof(cont));
  613. eeprom_read_block_wdtsafe(&cont.config, &eeprom_cont_config,
  614. sizeof(cont.config));
  615. /* Initialize and reset all pot states. */
  616. for (i = 0; i < ARRAY_SIZE(cont.pots); i++) {
  617. pot = &cont.pots[i];
  618. pot->nr = i;
  619. pot_reset(pot);
  620. }
  621. }