#include "app.h" #include "Debug.h" #include #if SM_BOARD_VERSION == 1 #define SENSOR_COUNT 2 #elif SM_BOARD_VERSION == 2 #define SENSOR_COUNT 4 #else #error "SM_BOARD_VERSION must be set. See config.h" #endif #define LED_PIN 13 #define EEPROM_MAGIC 0xa5 // See http://redbearlab.com/blendmicro/ for pins. 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 { 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"}, #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(); } } #ifdef SM_DEBUG == 1 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 == 1 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); } #if PERSISTENT_CONFIGURATION_SUPPORT == 1 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(": ->"); // Serial.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: