main.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. /*
  2. * Moistcontrol
  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 "main.h"
  21. #include "comm.h"
  22. #include "util.h"
  23. #include "sensor.h"
  24. #include "controller.h"
  25. #include "log.h"
  26. #include "twi_master.h"
  27. #include "pcf8574.h"
  28. #include "rv3029.h"
  29. #include "notify_led.h"
  30. #include "onoffswitch.h"
  31. #include <avr/io.h>
  32. #include <avr/wdt.h>
  33. /* RTC time fetch interval, in milliseconds. */
  34. #define RTC_FETCH_INTERVAL_MS 1000
  35. /* Message IDs of control messages transferred to and from
  36. * the host over serial wire. */
  37. enum user_message_id {
  38. MSG_LOG, /* Log message */
  39. MSG_LOG_FETCH, /* Log message request */
  40. MSG_RTC, /* RTC time */
  41. MSG_RTC_FETCH, /* RTC time request */
  42. MSG_CONTR_CONF, /* Global configuration */
  43. MSG_CONTR_CONF_FETCH, /* Global configuration request */
  44. MSG_CONTR_POT_CONF, /* Pot configuration */
  45. MSG_CONTR_POT_CONF_FETCH, /* Pot configuration request */
  46. MSG_CONTR_POT_STATE, /* Pot state */
  47. MSG_CONTR_POT_STATE_FETCH, /* Pot state request */
  48. MSG_CONTR_POT_REM_STATE, /* Pot remanent state */
  49. MSG_CONTR_POT_REM_STATE_FETCH, /* Pot remanent state request */
  50. MSG_MAN_MODE, /* Manual mode settings */
  51. MSG_MAN_MODE_FETCH, /* Manual mode settings request */
  52. MSG_CONTR_STATE, /* Global state */
  53. MSG_CONTR_STATE_FETCH, /* Global state request */
  54. };
  55. enum man_mode_flags {
  56. MANFLG_FREEZE_CHANGE = 1 << 0, /* Freeze change request */
  57. MANFLG_FREEZE_ENABLE = 1 << 1, /* Freeze on/off */
  58. MANFLG_NOTIFY_CHANGE = 1 << 2, /* LED-status change request */
  59. MANFLG_NOTIFY_ENABLE = 1 << 3, /* LED-status on/off */
  60. };
  61. enum contr_state_flags {
  62. CONTRSTAT_ONOFFSWITCH = 1 << 0, /* Hardware on/off-switch state. */
  63. CONTRSTAT_NOTIFLED = 1 << 1, /* Notification LED state. */
  64. };
  65. /* Payload of host communication messages. */
  66. struct msg_payload {
  67. /* The ID number. (enum user_message_id) */
  68. uint8_t id;
  69. union {
  70. /* Log message. */
  71. struct {
  72. struct log_item item;
  73. } _packed log;
  74. /* RTC time. */
  75. struct {
  76. struct rtc_time time;
  77. } _packed rtc;
  78. /* Global controller configuration. */
  79. struct {
  80. struct controller_global_config conf;
  81. } _packed contr_conf;
  82. /* Controller flower pot configuration. */
  83. struct {
  84. uint8_t pot_number;
  85. struct flowerpot_config conf;
  86. } _packed contr_pot_conf;
  87. /* Controller flower pot state. */
  88. struct {
  89. uint8_t pot_number;
  90. struct flowerpot_state state;
  91. } _packed contr_pot_state;
  92. /* Controller flower pot remanent state. */
  93. struct {
  94. uint8_t pot_number;
  95. struct flowerpot_remanent_state rem_state;
  96. } _packed contr_pot_rem_state;
  97. /* Manual mode settings. */
  98. struct {
  99. uint8_t force_stop_watering_mask;
  100. uint8_t valve_manual_mask;
  101. uint8_t valve_manual_state;
  102. uint8_t flags;
  103. uint8_t force_start_measurement_mask;
  104. } _packed manual_mode;
  105. /* Global controller state. */
  106. struct {
  107. uint8_t flags;
  108. } _packed contr_state;
  109. } _packed;
  110. } _packed;
  111. /* The current timekeeping count. */
  112. static jiffies_t jiffies_count;
  113. /* Serial communication timer. */
  114. static jiffies_t comm_timer;
  115. /* Timestamp for the next RTC time fetch. */
  116. static jiffies_t next_rtc_fetch;
  117. /* Host message handler.
  118. * Handle all received control messages sent by the host.
  119. */
  120. bool comm_handle_rx_message(const struct comm_message *msg,
  121. void *reply_payload)
  122. {
  123. const struct msg_payload *pl = comm_payload(const struct msg_payload *, msg);
  124. struct msg_payload *reply = reply_payload;
  125. bool ok;
  126. if (msg->fc & COMM_FC_ACK) {
  127. /* This is just an acknowledge. Ignore. */
  128. return 1;
  129. }
  130. switch (pl->id) {
  131. case MSG_LOG_FETCH: {
  132. /* Fetch of the first log item. */
  133. /* Fill the reply message. */
  134. reply->id = MSG_LOG;
  135. /* Get the first item from the log stack. */
  136. ok = log_pop(&reply->log.item);
  137. if (!ok) {
  138. /* No log available.
  139. * Signal an error to the host. */
  140. return 0;
  141. }
  142. break;
  143. }
  144. case MSG_RTC: {
  145. /* RTC time adjustment. */
  146. /* Write the new time to the RTC hardware. */
  147. rv3029_write_time(&pl->rtc.time);
  148. break;
  149. }
  150. case MSG_RTC_FETCH: {
  151. /* RTC time fetch. */
  152. /* Fill the reply message. */
  153. reply->id = MSG_RTC;
  154. rv3029_get_time(&reply->rtc.time);
  155. break;
  156. }
  157. case MSG_CONTR_CONF: {
  158. /* Set controller config. */
  159. struct controller_config conf;
  160. /* Get the current configuration. */
  161. controller_get_config(&conf);
  162. /* Write the new configuration. */
  163. conf.global = pl->contr_conf.conf;
  164. controller_update_config(&conf);
  165. break;
  166. }
  167. case MSG_CONTR_CONF_FETCH: {
  168. /* Fetch controller config. */
  169. struct controller_config conf;
  170. /* Get the current configuration. */
  171. controller_get_config(&conf);
  172. /* Fill the reply message. */
  173. reply->id = MSG_CONTR_CONF;
  174. reply->contr_conf.conf = conf.global;
  175. break;
  176. }
  177. case MSG_CONTR_POT_CONF: {
  178. /* Set flower pot config. */
  179. uint8_t pot_number = pl->contr_pot_conf.pot_number;
  180. struct controller_config conf;
  181. if (pot_number >= MAX_NR_FLOWERPOTS) {
  182. /* Invalid pot number. */
  183. return 0;
  184. }
  185. /* Get the current configuration. */
  186. controller_get_config(&conf);
  187. /* Write the new configuration. */
  188. conf.pots[pot_number] = pl->contr_pot_conf.conf;
  189. controller_update_config(&conf);
  190. break;
  191. }
  192. case MSG_CONTR_POT_CONF_FETCH: {
  193. /* Fetch flower pot config. */
  194. uint8_t pot_number = pl->contr_pot_conf.pot_number;
  195. struct controller_config conf;
  196. if (pot_number >= MAX_NR_FLOWERPOTS) {
  197. /* Invalid pot number. */
  198. return 0;
  199. }
  200. /* Get the current configuration. */
  201. controller_get_config(&conf);
  202. /* Fill the reply message. */
  203. reply->id = MSG_CONTR_POT_CONF;
  204. reply->contr_pot_conf.pot_number = pot_number;
  205. reply->contr_pot_conf.conf = conf.pots[pot_number];
  206. break;
  207. }
  208. case MSG_CONTR_POT_STATE_FETCH: {
  209. /* Fetch flower pot state. */
  210. uint8_t pot_number = pl->contr_pot_state.pot_number;
  211. if (pot_number >= MAX_NR_FLOWERPOTS) {
  212. /* Invalid pot number. */
  213. return 0;
  214. }
  215. /* Fill the reply message. */
  216. reply->id = MSG_CONTR_POT_STATE;
  217. reply->contr_pot_state.pot_number = pot_number;
  218. controller_get_pot_state(pot_number,
  219. &reply->contr_pot_state.state,
  220. NULL);
  221. break;
  222. }
  223. case MSG_CONTR_POT_REM_STATE: {
  224. /* Set the flower pot remanent state. */
  225. uint8_t pot_number = pl->contr_pot_rem_state.pot_number;
  226. if (pot_number >= MAX_NR_FLOWERPOTS) {
  227. /* Invalid pot number. */
  228. return 0;
  229. }
  230. /* Write the new rememanent state. */
  231. controller_update_pot_rem_state(pot_number,
  232. &pl->contr_pot_rem_state.rem_state);
  233. break;
  234. }
  235. case MSG_CONTR_POT_REM_STATE_FETCH: {
  236. /* Fetch flower pot remanent state. */
  237. uint8_t pot_number = pl->contr_pot_rem_state.pot_number;
  238. if (pot_number >= MAX_NR_FLOWERPOTS) {
  239. /* Invalid pot number. */
  240. return 0;
  241. }
  242. /* Fill the reply message. */
  243. reply->id = MSG_CONTR_POT_REM_STATE;
  244. reply->contr_pot_rem_state.pot_number = pot_number;
  245. controller_get_pot_state(pot_number,
  246. NULL,
  247. &reply->contr_pot_rem_state.rem_state);
  248. break;
  249. }
  250. case MSG_MAN_MODE: {
  251. /* Set controller manual mode state. */
  252. controller_manual_mode(pl->manual_mode.force_stop_watering_mask,
  253. pl->manual_mode.valve_manual_mask,
  254. pl->manual_mode.valve_manual_state,
  255. pl->manual_mode.force_start_measurement_mask);
  256. if (pl->manual_mode.flags & MANFLG_FREEZE_CHANGE)
  257. controller_freeze(!!(pl->manual_mode.flags & MANFLG_FREEZE_ENABLE));
  258. if (pl->manual_mode.flags & MANFLG_NOTIFY_CHANGE)
  259. notify_led_set(!!(pl->manual_mode.flags & MANFLG_NOTIFY_ENABLE));
  260. break;
  261. }
  262. case MSG_CONTR_STATE_FETCH: {
  263. /* Fetch global state. */
  264. enum onoff_state hw_switch;
  265. reply->id = MSG_CONTR_STATE;
  266. reply->contr_state.flags = 0;
  267. hw_switch = onoffswitch_get_state();
  268. if (hw_switch == ONOFF_IS_ON || hw_switch == ONOFF_SWITCHED_ON)
  269. reply->contr_state.flags |= CONTRSTAT_ONOFFSWITCH;
  270. if (notify_led_get())
  271. reply->contr_state.flags |= CONTRSTAT_NOTIFLED;
  272. break;
  273. }
  274. default:
  275. /* Unsupported message. Return failure. */
  276. return 0;
  277. }
  278. return 1;
  279. }
  280. /* 200 Hz system timer. */
  281. ISR(TIMER1_COMPA_vect)
  282. {
  283. mb();
  284. /* Increment the system time counter. */
  285. jiffies_count++;
  286. mb();
  287. }
  288. /* Get the current system time counter. */
  289. jiffies_t jiffies_get(void)
  290. {
  291. uint8_t sreg;
  292. jiffies_t j;
  293. /* Fetch system time counter with interrupts disabled. */
  294. sreg = irq_disable_save();
  295. j = jiffies_count;
  296. irq_restore(sreg);
  297. return j;
  298. }
  299. /* Initialize the system timer. */
  300. static void systimer_init(void)
  301. {
  302. /* Initialize timer-1 to 200 Hz interrupt frequency. */
  303. /* Set OC value for 16 Mhz CPU clock. */
  304. build_assert(F_CPU == 16000000ul);
  305. OCR1A = 1250;
  306. TCNT1 = 0;
  307. TCCR1A = 0;
  308. /* CTC mode, prescaler 64 */
  309. TCCR1B = (1 << WGM12) | (1 << CS10) | (1 << CS11);
  310. /* Enable OC interrupt. */
  311. TIMSK |= (1 << OCIE1A);
  312. }
  313. /* Handle realtime clock work. */
  314. static void handle_rtc(jiffies_t now)
  315. {
  316. /* Only, if the RTC fetch timer expired. */
  317. if (time_before(now, next_rtc_fetch))
  318. return;
  319. /* Re-trigger the RTC fetch timer. */
  320. next_rtc_fetch = now + msec_to_jiffies(RTC_FETCH_INTERVAL_MS);
  321. /* Read the current time from RTC. */
  322. rv3029_read_time();
  323. }
  324. /* Handle changes on the hardware on/off-switch state. */
  325. static enum onoff_state handle_onoffswitch(void)
  326. {
  327. enum onoff_state hw_switch;
  328. onoffswitch_work();
  329. hw_switch = onoffswitch_get_state();
  330. if (hw_switch == ONOFF_IS_OFF ||
  331. hw_switch == ONOFF_SWITCHED_OFF) {
  332. /* The controller is off.
  333. * Enable notification LED permanently. */
  334. notify_led_set(1);
  335. }
  336. if (hw_switch == ONOFF_SWITCHED_OFF) {
  337. /* Log the 'off'-event. */
  338. log_info(LOG_INFO_HWONOFF, 0);
  339. } else if (hw_switch == ONOFF_SWITCHED_ON) {
  340. /* Log the 'on'-event. */
  341. log_info(LOG_INFO_HWONOFF, 1);
  342. /* Disable notification LED.
  343. * This will also clear notification messages from other
  344. * sources (e.g. controller). */
  345. notify_led_set(0);
  346. }
  347. return hw_switch;
  348. }
  349. /* Program entry point. */
  350. int main(void) _mainfunc;
  351. int main(void)
  352. {
  353. jiffies_t now;
  354. irq_disable();
  355. wdt_enable(WDTO_2S);
  356. /* Initialize the system. */
  357. onoffswitch_init();
  358. notify_led_init();
  359. twi_init();
  360. systimer_init();
  361. rv3029_init();
  362. sensor_init();
  363. controller_init();
  364. comm_init();
  365. /* Sanity checks. */
  366. build_assert(sizeof(struct msg_payload) <= COMM_PAYLOAD_LEN);
  367. /* Enable watchdog, interrupts and enter the mainloop. */
  368. wdt_enable(WDTO_250MS);
  369. irq_enable();
  370. while (1) {
  371. /* Poke the watchdog. */
  372. wdt_reset();
  373. /* Get the current timestamp. */
  374. now = jiffies_get();
  375. /* Handle the state of the hardware on/off-switch. */
  376. handle_onoffswitch();
  377. /* Handle serial host communication. */
  378. comm_work();
  379. if (!time_before(now, comm_timer)) {
  380. comm_timer = now + msec_to_jiffies(10);
  381. comm_centisecond_tick();
  382. }
  383. /* Handle realtime clock work. */
  384. handle_rtc(now);
  385. /* Run the controller state machine. */
  386. controller_work();
  387. /* Handle notification LED state. */
  388. notify_led_work();
  389. }
  390. }