#include "app.h" #include #include #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 // http://bleaklow.com/2010/09/05/progmem_and_gcc_bug_34734.html #undef PROGMEM #define PROGMEM __attribute__((section(".progmem.data"))) #define LED_PIN 13 #define EEPROM_MAGIC 0x5a // 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, 10, 0, 9, "Sensor #1"}, { 10, 11, 12, 0, 10, 0, 9, "Sensor #2"}, #elif SM_BOARD_VERSION == 2 { A0, 1, 2, 0, 10, 0, 3, "wat"}, { A1, 3, 5, 0, 10, 0, 4, "woot"}, { A2, 8, 10, 0, 10, 0, 3, "foo"}, { A3, 11, 12, 0, 10, 0, 3, "bar"}, #endif }; static unsigned long next_update[SENSOR_COUNT]; // How often the connected light should toggle, in ms #define BLINK_WAIT 500 static boolean connected; static boolean blink; static unsigned long last_blink; #if PERSISTENT_CONFIGURATION_SUPPORT == 1 static boolean setup_settings(); static boolean load_settings(); static void store_settings(); static void set_default_settings(); #endif void sm_setup() { #if PERSISTENT_CONFIGURATION_SUPPORT == 1 setup_settings(); #endif 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); } void sm_on_connect() { // Disable all updates until enabled. for (int i = 0; i < SENSOR_COUNT; i++) { next_update[i] = 0; } connected = true; blink = true; digitalWrite(LED_PIN, HIGH); last_blink = millis(); } void sm_on_disconnect() { connected = false; digitalWrite(LED_PIN, LOW); } #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++, chars[i]); } return index + 1 + len; } static int load_string(int index, uint8_t max_length, uint8_t& length, char* chars) { uint8_t stored_len = EEPROM.read(index++); uint8_t len = min(stored_len, max_length); for (int i = 0; i < stored_len; i++) { chars[i] = EEPROM.read(index++); } length = len; return stored_len; } static void set_default_settings() { #if SENSOR_COUNT > 10 #error SENSOR_COUNT cannot be bigger than 10 #endif for (int i = 0; i < SENSOR_COUNT; i++) { sensors[i].warning_value = 700; int idx = 8; strcpy(sensors[i].name, "123456 #"); sensors[i].name[idx++] = '0' + i; sensors[i].name_length = idx; } } /** * 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; s.name_length = EEPROM.read(index++); 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); } Serial.println("Settings saved"); } static void setup_settings() { /* for (int i = 0; i < 3; i++) { for (int j = 0; j < 16; j++) { uint8_t value = EEPROM.read(i * 16 + j); Serial.print(value, HEX); Serial.print(" "); Serial.print((char)value); Serial.print(" "); } Serial.println(); } for (int i = 0; i < SENSOR_COUNT; i++) { struct sm_sensor s = sensors[i]; Serial.print("name len="); Serial.print(s.name_length, DEC); Serial.print(": ->"); Serial.write((const uint8_t*) s.name, s.name_length); Serial.print("<-"); Serial.println(); } */ // set_default_settings(); /* if (!load_settings()) { Serial.println("setting default settings"); set_default_settings(); Serial.println("saving settings"); store_settings(); } else { Serial.println("Settings loaded"); } */ for (int i = 0; i < SENSOR_COUNT; i++) { Serial.print("XX: i="); Serial.print(i, DEC); struct sm_sensor& s = sensors[i]; Serial.print(", name len="); Serial.print(s.name_length, DEC); Serial.print(": ->"); Serial.write((const uint8_t*) s.name, s.name_length); Serial.print("<-"); Serial.println(); } /**/ Serial.println("done"); } #endif // PERSISTENT_CONFIGURATION_SUPPORT void sm_loop() { static unsigned long last = 0, now; static uint8_t x = 10; now = millis(); if (now - last_blink > BLINK_WAIT) { last_blink = now; if (connected) { blink = !blink; digitalWrite(LED_PIN, blink ? HIGH : LOW); } } if (now - last > 3000) { last = now; for (int i = 0; i < SENSOR_COUNT; i++) { struct sm_sensor& s = sensors[i]; digitalWrite(s.d1_pin, HIGH); digitalWrite(s.d2_pin, LOW); s.value = analogRead(s.a_pin); if (i > 0) { Serial.print(", "); } Serial.print("#"); Serial.print(i, DEC); Serial.print(" = "); Serial.print(s.value, DEC); } Serial.println(); } // TODO: implement proper notification if (false && now - last > 3000) { last = now; struct sm_res res; res.code = SM_CMD_GET_VALUE; res.get_value.value = x; x++; if(x == 0) { x = 10; } // if(x % 2 == 0) { // notify_soil_moisture(res); // } else { // notify_battery_level(x); // } notify_soil_moisture(res, sizeof(sm_get_value_res)); } } #ifdef SM_DEBUG static void write_name(uint8_t const* name, uint8_t len) { for(int i = 0; i < len; i++) { Serial.print((char)name[i]); } } void write_req(struct sm_req const& req) { Serial.print(">> "); switch(req.code) { case SM_CMD_GET_SENSOR_COUNT: Serial.print("SM_CMD_GET_SENSOR_COUNT"); break; case SM_CMD_GET_VALUE: Serial.print("SM_CMD_GET_VALUE"); Serial.print(": sensor="); Serial.print(req.get_value.sensor, DEC); break; case SM_CMD_SET_WARNING_VALUE: Serial.print("SM_CMD_SET_WARNING_VALUE"); Serial.print(": sensor="); Serial.print(req.set_warning_value.sensor, DEC); Serial.print(", warning_value="); Serial.print(req.set_warning_value.warning_value, DEC); break; case SM_CMD_GET_WARNING_VALUE: Serial.print("SM_CMD_GET_WARNING_VALUE"); Serial.print(": sensor="); Serial.print(req.get_warning_value.sensor, DEC); break; case SM_CMD_SET_SENSOR_NAME: Serial.print("SM_CMD_SET_SENSOR_NAME"); Serial.print(": sensor="); Serial.print(req.set_sensor_name.sensor, DEC); Serial.print(", name="); write_name(req.set_sensor_name.name, req.set_sensor_name.length); break; case SM_CMD_GET_SENSOR_NAME: Serial.print("SM_CMD_GET_SENSOR_NAME"); break; case SM_CMD_SET_UPDATE_INTERVAL: Serial.print("SM_CMD_SET_UPDATE_INTERVAL"); Serial.print(": interval_in_seconds="); Serial.print(req.set_update_interval.interval_in_seconds, DEC); break; default: Serial.print("Unknown command: "); Serial.print(req.code); } Serial.println(); } void write_res(struct sm_res const& res) { Serial.print("<< "); switch(res.code) { case SM_CMD_GET_SENSOR_COUNT: Serial.print("SM_CMD_GET_SENSOR_COUNT"); Serial.print(": count="); Serial.print(res.get_sensor_count.count, DEC); break; case SM_CMD_GET_VALUE: Serial.print("SM_CMD_GET_VALUE"); Serial.print(": value="); Serial.print(res.get_value.value, DEC); break; case SM_CMD_SET_WARNING_VALUE: Serial.print("SM_CMD_SET_WARNING_VALUE"); break; case SM_CMD_GET_WARNING_VALUE: Serial.print("SM_CMD_GET_WARNING_VALUE"); Serial.print(": warning_value="); Serial.print(res.get_warning_value.warning_value, DEC); break; case SM_CMD_SET_SENSOR_NAME: Serial.print("SM_CMD_SET_SENSOR_NAME"); break; case SM_CMD_GET_SENSOR_NAME: Serial.print("SM_CMD_GET_SENSOR_NAME"); Serial.print(": name="); write_name(res.get_sensor_name.name, res.get_sensor_name.length); break; case SM_CMD_SET_UPDATE_INTERVAL: Serial.print("SM_CMD_SET_UPDATE_INTERVAL"); break; default: Serial.print("Unknown command: "); Serial.print(res.code); } Serial.println(); } #endif 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 res.code = req->code; uint8_t body_len; switch(req->code) { case SM_CMD_GET_SENSOR_COUNT: body_len = sizeof(sm_get_sensor_count_res); 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) { // 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) { 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.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) { 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.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; default: body_len = 0; res.code = SM_CMD_FAIL; break; } if (res.code == SM_CMD_FAIL) { body_len = 0; } notify_soil_moisture(res, body_len); } // vim: set ft=arduino: