#include "app.h" #include "Debug.h" #include "config-check.h" #ifdef PERSISTENT_CONFIGURATION_SUPPORT #include #endif #if SM_BOARD_VERSION == 1 #define SENSOR_COUNT 2 #elif SM_BOARD_VERSION == 2 #define SENSOR_COUNT 4 #elif SM_BOARD_VERSION == 3 #define SENSOR_COUNT 1 #elif SM_BOARD_VERSION == 4 #define SENSOR_COUNT 0 #else #error "SM_BOARD_VERSION must be set to a valid value. See config-check.h" #endif #define LED_PIN 13 #define EEPROM_MAGIC 0xa5 struct sm_sensor { uint8_t a_pin; uint8_t d1_pin; uint8_t d2_pin; uint16_t value; uint16_t warning_value; uint16_t update_interval; uint8_t name_length; char name[SENSOR_NAME_LEN]; } sensors[SENSOR_COUNT] = { // A Pin, D1 Pin, D2 Pin, Value, Warning Value, Update Interval, Length of Name, Name #if SM_BOARD_VERSION == 1 // See http://redbearlab.com/blendmicro/ for pins. { 8, 3, 5, 0, 200, 3000, 9, "Sensor #1"}, { 10, 11, 12, 0, 200, 5000, 9, "Sensor #2"}, #elif SM_BOARD_VERSION == 2 { A0, 1, 2, 0, 200, 3000, 3, "wat"}, { A1, 3, 5, 0, 200, 3000, 4, "woot"}, { A2, 8, 10, 0, 200, 3000, 3, "foo"}, { A3, 11, 12, 0, 200, 3000, 3, "bar"}, #elif SM_BOARD_VERSION == 3 { A0, 1, 2, 0, 200, 3000, 3, "0"}, #elif SM_BOARD_VERSION == 4 // TODO: add some sensors #endif }; static unsigned long next_update[SENSOR_COUNT]; // How often the connected light should toggle, in ms #define BLINK_WAIT 500 static bool connected; static bool blink; static unsigned long last_blink; static void setup_settings(); void sm_setup() { setup_settings(); for (int i = 0; i < SENSOR_COUNT; i++) { struct sm_sensor s = sensors[i]; pinMode(s.a_pin, INPUT); pinMode(s.d1_pin, OUTPUT); pinMode(s.d2_pin, OUTPUT); } pinMode(LED_PIN, OUTPUT); pinMode(2, OUTPUT); digitalWrite(2, HIGH); debug.println("sm_setup: done"); } void sm_on_connect() { unsigned long now = millis(); // Disable all updates until enabled. for (int i = 0; i < SENSOR_COUNT; i++) { next_update[i] = now + sensors[i].update_interval; } connected = true; blink = true; digitalWrite(LED_PIN, HIGH); last_blink = millis(); } void sm_on_disconnect() { connected = false; digitalWrite(LED_PIN, LOW); } void sm_loop() { unsigned long now = millis(); // TODO: make the blink configurable. It's nice for debugging, bad for power usage when continuously connected if (now - last_blink >= BLINK_WAIT) { last_blink = now; if (connected) { blink = !blink; digitalWrite(LED_PIN, blink ? HIGH : LOW); } } // TODO: there is no reason to read the values unless someone are connected *and* have asked for notifications. int count = 0; for (uint8_t i = 0; i < SENSOR_COUNT; i++) { struct sm_sensor &s = sensors[i]; if (now < next_update[i]) { continue; } next_update[i] += s.update_interval; digitalWrite(s.d1_pin, HIGH); digitalWrite(s.d2_pin, LOW); s.value = (uint16_t) analogRead(s.a_pin); if (count++ > 0) { debug.print(", "); } debug.print("#"); debug.print(i, DEC); debug.print(" = "); debug.print(s.value, DEC); struct sm_res res; res.code = SM_CMD_GET_VALUE; res.get_value.sensor = i; res.get_value.value = s.value; notify_soil_moisture(res, sizeof(sm_get_value_res)); } if (count > 0) { debug.println(); } } #if SM_DEBUG static void write_name(uint8_t const* name, uint8_t len) { for (int i = 0; i < len; i++) { debug.print((char)name[i]); } } void write_req(struct sm_req const &req) { debug.print(">> "); switch (req.code) { case SM_CMD_GET_SENSOR_COUNT: debug.print("SM_CMD_GET_SENSOR_COUNT"); break; case SM_CMD_GET_VALUE: debug.print("SM_CMD_GET_VALUE"); debug.print(": sensor="); debug.print(req.get_value.sensor, DEC); break; case SM_CMD_SET_WARNING_VALUE: debug.print("SM_CMD_SET_WARNING_VALUE"); debug.print(": sensor="); debug.print(req.set_warning_value.sensor, DEC); debug.print(", warning_value="); debug.print(req.set_warning_value.warning_value, DEC); break; case SM_CMD_GET_WARNING_VALUE: debug.print("SM_CMD_GET_WARNING_VALUE"); debug.print(": sensor="); debug.print(req.get_warning_value.sensor, DEC); break; case SM_CMD_SET_SENSOR_NAME: debug.print("SM_CMD_SET_SENSOR_NAME"); debug.print(": sensor="); debug.print(req.set_sensor_name.sensor, DEC); debug.print(", name="); write_name(req.set_sensor_name.name, req.set_sensor_name.length); break; case SM_CMD_GET_SENSOR_NAME: debug.print("SM_CMD_GET_SENSOR_NAME"); break; case SM_CMD_SET_UPDATE_INTERVAL: debug.print("SM_CMD_SET_UPDATE_INTERVAL"); debug.print(": interval_in_seconds="); debug.print(req.set_update_interval.interval_in_seconds, DEC); break; default: debug.print("Unknown command: "); debug.print(req.code); } debug.println(); } void write_res(struct sm_res const &res) { debug.print("<< "); switch (res.code) { case SM_CMD_GET_SENSOR_COUNT: debug.print("SM_CMD_GET_SENSOR_COUNT"); debug.print(": count="); debug.print(res.get_sensor_count.count, DEC); break; case SM_CMD_GET_VALUE: debug.print("SM_CMD_GET_VALUE"); debug.print(": sensor="); debug.print(res.get_value.sensor, DEC); debug.print(", value="); debug.print(res.get_value.value, DEC); break; case SM_CMD_SET_WARNING_VALUE: debug.print("SM_CMD_SET_WARNING_VALUE"); break; case SM_CMD_GET_WARNING_VALUE: debug.print("SM_CMD_GET_WARNING_VALUE"); debug.print(": warning_value="); debug.print(res.get_warning_value.warning_value, DEC); break; case SM_CMD_SET_SENSOR_NAME: debug.print("SM_CMD_SET_SENSOR_NAME"); break; case SM_CMD_GET_SENSOR_NAME: debug.print("SM_CMD_GET_SENSOR_NAME"); debug.print(": name="); write_name(res.get_sensor_name.name, res.get_sensor_name.length); break; case SM_CMD_SET_UPDATE_INTERVAL: debug.print("SM_CMD_SET_UPDATE_INTERVAL"); break; default: debug.print("Unknown command: "); debug.print(res.code); } debug.println(); } #endif // SM_DEBUG static inline bool len_ok(uint8_t sizeof_packet, uint8_t sizeof_struct) { return sizeof_packet >= sizeof_struct; } void on_soil_moisture_ctrl(uint8_t *data, uint8_t len) { struct sm_req *req = (struct sm_req *) data; struct sm_res res; uint8_t sensor; #if SM_DEBUG write_req(*req); #endif if (len <= 1) { return; } res.code = req->code; uint8_t body_len = 0; res.code = SM_CMD_FAIL; // TODO: use len_op(len, sizeof(sm_XX_req)) to check that each request's size is ok. switch (req->code) { case SM_CMD_GET_SENSOR_COUNT: body_len = sizeof(sm_get_sensor_count_res); res.code = req->code; res.get_sensor_count.count = SENSOR_COUNT; break; case SM_CMD_GET_VALUE: body_len = sizeof(sm_get_value_res); sensor = req->get_value.sensor; if (sensor < SENSOR_COUNT) { res.code = req->code; // TODO: update the sensor's value res.get_value.value = sensors[sensor].value; } else { res.code = SM_CMD_FAIL; } break; case SM_CMD_SET_WARNING_VALUE: body_len = sizeof(sm_set_warning_value_res); sensor = req->set_warning_value.sensor; if (sensor < SENSOR_COUNT) { res.code = req->code; sensors[sensor].warning_value = req->set_warning_value.warning_value; } else { res.code = SM_CMD_FAIL; } break; case SM_CMD_GET_WARNING_VALUE: body_len = sizeof(sm_get_warning_value_res); sensor = req->get_warning_value.sensor; if (sensor < SENSOR_COUNT) { res.code = req->code; res.get_warning_value.warning_value = sensors[sensor].warning_value; } else { res.code = SM_CMD_FAIL; } break; case SM_CMD_SET_SENSOR_NAME: body_len = sizeof(sm_set_sensor_name_res); sensor = req->set_sensor_name.sensor; if (sensor < SENSOR_COUNT) { res.code = req->code; sensors[sensor].name_length = min(req->set_sensor_name.length, SENSOR_NAME_LEN); memcpy(sensors[sensor].name, req->set_sensor_name.name, sensors[sensor].name_length); } else { res.code = SM_CMD_FAIL; } break; case SM_CMD_GET_SENSOR_NAME: body_len = sizeof(sm_get_sensor_name_res); sensor = req->get_sensor_name.sensor; if (sensor < SENSOR_COUNT) { res.code = req->code; res.get_sensor_name.length = sensors[sensor].name_length; memcpy(res.get_sensor_name.name, sensors[sensor].name, SENSOR_NAME_LEN); } else { res.code = SM_CMD_FAIL; } break; case SM_CMD_SET_UPDATE_INTERVAL: body_len = sizeof(sm_set_update_interval_res); sensor = req->set_update_interval.sensor; if (sensor < SENSOR_COUNT) { sensors[sensor].update_interval = req->set_update_interval.interval_in_seconds; } else { res.code = SM_CMD_FAIL; } break; } if (res.code == SM_CMD_FAIL) { body_len = 0; } notify_soil_moisture(res, body_len); } #ifdef PERSISTENT_CONFIGURATION_SUPPORT static int store_string(int index, uint8_t len, char *chars) { EEPROM.write(index++, len); for (int i = 0; i < len; i++) { EEPROM.write(index++, (uint8_t) chars[i]); } return 1 + len; } static int load_string(int index, const uint8_t max_length, uint8_t &length, char *chars) { length = EEPROM.read(index++); uint8_t len = min(length, max_length); for (int i = 0; i < len; i++) { chars[i] = (char) EEPROM.read(index++); } return 1 + len; } /** * Returns true if the settings was successfully read. */ static boolean load_settings() { int index = 0; uint8_t magic = EEPROM.read(index++); // Check to see if the EEPROM contains a magic byte indicating if we have written anything before. if (magic != EEPROM_MAGIC) { return false; } for (int i = 0; i < SENSOR_COUNT; i++) { struct sm_sensor s = sensors[i]; s.warning_value = EEPROM.read(index++); s.warning_value += EEPROM.read(index++) << 8; index += load_string(index, SENSOR_NAME_LEN, s.name_length, s.name); } return true; } static void store_settings() { int index = 0; EEPROM.write(index++, EEPROM_MAGIC); for (int i = 0; i < SENSOR_COUNT; i++) { struct sm_sensor s = sensors[i]; EEPROM.write(index++, s.warning_value); EEPROM.write(index++, s.warning_value >> 8); index += store_string(index, s.name_length, s.name); } debug.println("Settings saved"); } static void setup_settings() { if (!load_settings()) { debug.println("Could not load settings, storing defaults"); store_settings(); } else { debug.println("Settings loaded"); } for (int i = 0; i < SENSOR_COUNT; i++) { debug.print("Sensor #"); debug.print(i, DEC); struct sm_sensor &s = sensors[i]; // debug.print(", name len="); // debug.print(s.name_length, DEC); // debug.print(": ->"); // debug.write((const uint8_t*) s.name, s.name_length); // debug.print("<-"); debug.print(": update_interval="); debug.print(s.update_interval, DEC); debug.print(", warning_value="); debug.print(s.warning_value, DEC); debug.println(); } debug.println("setup_settings: done"); } #else static void setup_settings() { } #endif // PERSISTENT_CONFIGURATION_SUPPORT // vim: set ft=arduino: