123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- /*
- * Moistcontrol
- *
- * Copyright (c) 2013 Michael Buesch <m@bues.ch>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
- #include "main.h"
- #include "comm.h"
- #include "util.h"
- #include "sensor.h"
- #include "controller.h"
- #include "log.h"
- #include "twi_master.h"
- #include "pcf8574.h"
- #include "rv3029.h"
- #include "notify_led.h"
- #include "onoffswitch.h"
- #include <avr/io.h>
- #include <avr/wdt.h>
- /* RTC time fetch interval, in milliseconds. */
- #define RTC_FETCH_INTERVAL_MS 1000
- /* Message IDs of control messages transferred to and from
- * the host over serial wire. */
- enum user_message_id {
- MSG_LOG, /* Log message */
- MSG_LOG_FETCH, /* Log message request */
- MSG_RTC, /* RTC time */
- MSG_RTC_FETCH, /* RTC time request */
- MSG_CONTR_CONF, /* Global configuration */
- MSG_CONTR_CONF_FETCH, /* Global configuration request */
- MSG_CONTR_POT_CONF, /* Pot configuration */
- MSG_CONTR_POT_CONF_FETCH, /* Pot configuration request */
- MSG_CONTR_POT_STATE, /* Pot state */
- MSG_CONTR_POT_STATE_FETCH, /* Pot state request */
- MSG_CONTR_POT_REM_STATE, /* Pot remanent state */
- MSG_CONTR_POT_REM_STATE_FETCH, /* Pot remanent state request */
- MSG_MAN_MODE, /* Manual mode settings */
- MSG_MAN_MODE_FETCH, /* Manual mode settings request */
- MSG_CONTR_STATE, /* Global state */
- MSG_CONTR_STATE_FETCH, /* Global state request */
- };
- enum man_mode_flags {
- MANFLG_FREEZE_CHANGE = 1 << 0, /* Freeze change request */
- MANFLG_FREEZE_ENABLE = 1 << 1, /* Freeze on/off */
- MANFLG_NOTIFY_CHANGE = 1 << 2, /* LED-status change request */
- MANFLG_NOTIFY_ENABLE = 1 << 3, /* LED-status on/off */
- };
- enum contr_state_flags {
- CONTRSTAT_ONOFFSWITCH = 1 << 0, /* Hardware on/off-switch state. */
- CONTRSTAT_NOTIFLED = 1 << 1, /* Notification LED state. */
- };
- /* Payload of host communication messages. */
- struct msg_payload {
- /* The ID number. (enum user_message_id) */
- uint8_t id;
- union {
- /* Log message. */
- struct {
- struct log_item item;
- } _packed log;
- /* RTC time. */
- struct {
- struct rtc_time time;
- } _packed rtc;
- /* Global controller configuration. */
- struct {
- struct controller_global_config conf;
- } _packed contr_conf;
- /* Controller flower pot configuration. */
- struct {
- uint8_t pot_number;
- struct flowerpot_config conf;
- } _packed contr_pot_conf;
- /* Controller flower pot state. */
- struct {
- uint8_t pot_number;
- struct flowerpot_state state;
- } _packed contr_pot_state;
- /* Controller flower pot remanent state. */
- struct {
- uint8_t pot_number;
- struct flowerpot_remanent_state rem_state;
- } _packed contr_pot_rem_state;
- /* Manual mode settings. */
- struct {
- uint8_t force_stop_watering_mask;
- uint8_t valve_manual_mask;
- uint8_t valve_manual_state;
- uint8_t flags;
- uint8_t force_start_measurement_mask;
- } _packed manual_mode;
- /* Global controller state. */
- struct {
- uint8_t flags;
- } _packed contr_state;
- } _packed;
- } _packed;
- /* The current timekeeping count. */
- static jiffies_t jiffies_count;
- /* Serial communication timer. */
- static jiffies_t comm_timer;
- /* Timestamp for the next RTC time fetch. */
- static jiffies_t next_rtc_fetch;
- /* Host message handler.
- * Handle all received control messages sent by the host.
- */
- bool comm_handle_rx_message(const struct comm_message *msg,
- void *reply_payload)
- {
- const struct msg_payload *pl = comm_payload(const struct msg_payload *, msg);
- struct msg_payload *reply = reply_payload;
- bool ok;
- if (msg->fc & COMM_FC_ACK) {
- /* This is just an acknowledge. Ignore. */
- return 1;
- }
- switch (pl->id) {
- case MSG_LOG_FETCH: {
- /* Fetch of the first log item. */
- /* Fill the reply message. */
- reply->id = MSG_LOG;
- /* Get the first item from the log stack. */
- ok = log_pop(&reply->log.item);
- if (!ok) {
- /* No log available.
- * Signal an error to the host. */
- return 0;
- }
- break;
- }
- case MSG_RTC: {
- /* RTC time adjustment. */
- /* Write the new time to the RTC hardware. */
- rv3029_write_time(&pl->rtc.time);
- break;
- }
- case MSG_RTC_FETCH: {
- /* RTC time fetch. */
- /* Fill the reply message. */
- reply->id = MSG_RTC;
- rv3029_get_time(&reply->rtc.time);
- break;
- }
- case MSG_CONTR_CONF: {
- /* Set controller config. */
- struct controller_config conf;
- /* Get the current configuration. */
- controller_get_config(&conf);
- /* Write the new configuration. */
- conf.global = pl->contr_conf.conf;
- controller_update_config(&conf);
- break;
- }
- case MSG_CONTR_CONF_FETCH: {
- /* Fetch controller config. */
- struct controller_config conf;
- /* Get the current configuration. */
- controller_get_config(&conf);
- /* Fill the reply message. */
- reply->id = MSG_CONTR_CONF;
- reply->contr_conf.conf = conf.global;
- break;
- }
- case MSG_CONTR_POT_CONF: {
- /* Set flower pot config. */
- uint8_t pot_number = pl->contr_pot_conf.pot_number;
- struct controller_config conf;
- if (pot_number >= MAX_NR_FLOWERPOTS) {
- /* Invalid pot number. */
- return 0;
- }
- /* Get the current configuration. */
- controller_get_config(&conf);
- /* Write the new configuration. */
- conf.pots[pot_number] = pl->contr_pot_conf.conf;
- controller_update_config(&conf);
- break;
- }
- case MSG_CONTR_POT_CONF_FETCH: {
- /* Fetch flower pot config. */
- uint8_t pot_number = pl->contr_pot_conf.pot_number;
- struct controller_config conf;
- if (pot_number >= MAX_NR_FLOWERPOTS) {
- /* Invalid pot number. */
- return 0;
- }
- /* Get the current configuration. */
- controller_get_config(&conf);
- /* Fill the reply message. */
- reply->id = MSG_CONTR_POT_CONF;
- reply->contr_pot_conf.pot_number = pot_number;
- reply->contr_pot_conf.conf = conf.pots[pot_number];
- break;
- }
- case MSG_CONTR_POT_STATE_FETCH: {
- /* Fetch flower pot state. */
- uint8_t pot_number = pl->contr_pot_state.pot_number;
- if (pot_number >= MAX_NR_FLOWERPOTS) {
- /* Invalid pot number. */
- return 0;
- }
- /* Fill the reply message. */
- reply->id = MSG_CONTR_POT_STATE;
- reply->contr_pot_state.pot_number = pot_number;
- controller_get_pot_state(pot_number,
- &reply->contr_pot_state.state,
- NULL);
- break;
- }
- case MSG_CONTR_POT_REM_STATE: {
- /* Set the flower pot remanent state. */
- uint8_t pot_number = pl->contr_pot_rem_state.pot_number;
- if (pot_number >= MAX_NR_FLOWERPOTS) {
- /* Invalid pot number. */
- return 0;
- }
- /* Write the new rememanent state. */
- controller_update_pot_rem_state(pot_number,
- &pl->contr_pot_rem_state.rem_state);
- break;
- }
- case MSG_CONTR_POT_REM_STATE_FETCH: {
- /* Fetch flower pot remanent state. */
- uint8_t pot_number = pl->contr_pot_rem_state.pot_number;
- if (pot_number >= MAX_NR_FLOWERPOTS) {
- /* Invalid pot number. */
- return 0;
- }
- /* Fill the reply message. */
- reply->id = MSG_CONTR_POT_REM_STATE;
- reply->contr_pot_rem_state.pot_number = pot_number;
- controller_get_pot_state(pot_number,
- NULL,
- &reply->contr_pot_rem_state.rem_state);
- break;
- }
- case MSG_MAN_MODE: {
- /* Set controller manual mode state. */
- controller_manual_mode(pl->manual_mode.force_stop_watering_mask,
- pl->manual_mode.valve_manual_mask,
- pl->manual_mode.valve_manual_state,
- pl->manual_mode.force_start_measurement_mask);
- if (pl->manual_mode.flags & MANFLG_FREEZE_CHANGE)
- controller_freeze(!!(pl->manual_mode.flags & MANFLG_FREEZE_ENABLE));
- if (pl->manual_mode.flags & MANFLG_NOTIFY_CHANGE)
- notify_led_set(!!(pl->manual_mode.flags & MANFLG_NOTIFY_ENABLE));
- break;
- }
- case MSG_CONTR_STATE_FETCH: {
- /* Fetch global state. */
- enum onoff_state hw_switch;
- reply->id = MSG_CONTR_STATE;
- reply->contr_state.flags = 0;
- hw_switch = onoffswitch_get_state();
- if (hw_switch == ONOFF_IS_ON || hw_switch == ONOFF_SWITCHED_ON)
- reply->contr_state.flags |= CONTRSTAT_ONOFFSWITCH;
- if (notify_led_get())
- reply->contr_state.flags |= CONTRSTAT_NOTIFLED;
- break;
- }
- default:
- /* Unsupported message. Return failure. */
- return 0;
- }
- return 1;
- }
- /* 200 Hz system timer. */
- ISR(TIMER1_COMPA_vect)
- {
- mb();
- /* Increment the system time counter. */
- jiffies_count++;
- mb();
- }
- /* Get the current system time counter. */
- jiffies_t jiffies_get(void)
- {
- uint8_t sreg;
- jiffies_t j;
- /* Fetch system time counter with interrupts disabled. */
- sreg = irq_disable_save();
- j = jiffies_count;
- irq_restore(sreg);
- return j;
- }
- /* Initialize the system timer. */
- static void systimer_init(void)
- {
- /* Initialize timer-1 to 200 Hz interrupt frequency. */
- /* Set OC value for 16 Mhz CPU clock. */
- build_assert(F_CPU == 16000000ul);
- OCR1A = 1250;
- TCNT1 = 0;
- TCCR1A = 0;
- /* CTC mode, prescaler 64 */
- TCCR1B = (1 << WGM12) | (1 << CS10) | (1 << CS11);
- /* Enable OC interrupt. */
- TIMSK |= (1 << OCIE1A);
- }
- /* Handle realtime clock work. */
- static void handle_rtc(jiffies_t now)
- {
- /* Only, if the RTC fetch timer expired. */
- if (time_before(now, next_rtc_fetch))
- return;
- /* Re-trigger the RTC fetch timer. */
- next_rtc_fetch = now + msec_to_jiffies(RTC_FETCH_INTERVAL_MS);
- /* Read the current time from RTC. */
- rv3029_read_time();
- }
- /* Handle changes on the hardware on/off-switch state. */
- static enum onoff_state handle_onoffswitch(void)
- {
- enum onoff_state hw_switch;
- onoffswitch_work();
- hw_switch = onoffswitch_get_state();
- if (hw_switch == ONOFF_IS_OFF ||
- hw_switch == ONOFF_SWITCHED_OFF) {
- /* The controller is off.
- * Enable notification LED permanently. */
- notify_led_set(1);
- }
- if (hw_switch == ONOFF_SWITCHED_OFF) {
- /* Log the 'off'-event. */
- log_info(LOG_INFO_HWONOFF, 0);
- } else if (hw_switch == ONOFF_SWITCHED_ON) {
- /* Log the 'on'-event. */
- log_info(LOG_INFO_HWONOFF, 1);
- /* Disable notification LED.
- * This will also clear notification messages from other
- * sources (e.g. controller). */
- notify_led_set(0);
- }
- return hw_switch;
- }
- /* Program entry point. */
- int main(void) _mainfunc;
- int main(void)
- {
- jiffies_t now;
- irq_disable();
- wdt_enable(WDTO_2S);
- /* Initialize the system. */
- onoffswitch_init();
- notify_led_init();
- twi_init();
- systimer_init();
- rv3029_init();
- sensor_init();
- controller_init();
- comm_init();
- /* Sanity checks. */
- build_assert(sizeof(struct msg_payload) <= COMM_PAYLOAD_LEN);
- /* Enable watchdog, interrupts and enter the mainloop. */
- wdt_enable(WDTO_250MS);
- irq_enable();
- while (1) {
- /* Poke the watchdog. */
- wdt_reset();
- /* Get the current timestamp. */
- now = jiffies_get();
- /* Handle the state of the hardware on/off-switch. */
- handle_onoffswitch();
- /* Handle serial host communication. */
- comm_work();
- if (!time_before(now, comm_timer)) {
- comm_timer = now + msec_to_jiffies(10);
- comm_centisecond_tick();
- }
- /* Handle realtime clock work. */
- handle_rtc(now);
- /* Run the controller state machine. */
- controller_work();
- /* Handle notification LED state. */
- notify_led_work();
- }
- }
|