/** * The MySensors Arduino library handles the wireless radio link and protocol * between your home built sensors/actuators and HA controller of choice. * The sensors forms a self healing radio network with optional repeaters. Each * repeater and gateway builds a routing tables in EEPROM which keeps track of the * network topology allowing messages to be routed to nodes. */ // Enable debug prints to serial monitor //#define MY_DEBUG // configure radio #define MY_RADIO_RFM69 /** @brief RFM69 frequency to use (RF69_433MHZ for 433MHz, RF69_868MHZ for 868MHz or RF69_915MHZ for 915MHz). */ #define MY_RFM69_FREQUENCY RF69_868MHZ /** @brief Enable this if you're running the RFM69HW model. */ //#define MY_IS_RFM69HW /** @brief RFM69 Network ID. Use the same for all nodes that will talk to each other. */ #define MY_RFM69_NETWORKID 1 /** @brief Node id defaults to AUTO (tries to fetch id from controller). */ #define MY_NODE_ID 1 /** @brief If set, transport traffic is unmonitored and GW connection is optional */ #define MY_TRANSPORT_DONT_CARE_MODE /** @brief Node parent defaults to AUTO (tries to find a parent automatically). */ #define MY_PARENT_NODE_ID 0 /** @brief The user-defined AES key to use for EEPROM personalization */ #include "aes_key.h" // Enable repeater functionality for this node //#define MY_REPEATER_FEATURE /** @brief Enables RFM69 automatic transmit power control class. */ //#define MY_RFM69_ATC #ifdef MY_AES_KEY /** @brief enables RFM69 encryption */ #define MY_RFM69_ENABLE_ENCRYPTION #endif #include #include #include enum sensor_type : uint8_t { SENSOR_RELAY = (1u << 0), SENSOR_DIMMER = (1u << 1), SENSOR_BUTTON = (1u << 2), }; struct sensor_t { uint8_t id; uint8_t type; struct { uint8_t pin; // relay pin } relay; struct { uint8_t pin; // push button pin Bounce bounce; } button; }; struct sensor_t sensors[] = { { .id = 0, .type = SENSOR_RELAY | SENSOR_BUTTON, .relay = { .pin = 4 }, .button = { .pin = 6, .bounce = Bounce() }, }, { .id = 1, .type = SENSOR_RELAY | SENSOR_BUTTON, .relay = { .pin = 5 }, .button = { .pin = 7, .bounce = Bounce() }, }, }; //#define SAVE_RESTORE #define NUM(a) (sizeof(a) / sizeof(*a)) #define RELAY_ON 1 // GPIO value to write to turn on attached relay #define RELAY_OFF 0 // GPIO value to write to turn off attached relay #define TEMP_SENSOR_ID 254 #define TEMP_READ_INTERVAL 1000L // read temp every 1 sec #define TEMP_N_READS_MSG 60*60 // force temp message every n reads #define TEMP_OFFSET 0 MyMessage msg(0, V_STATUS); inline void checkTemperature(void); bool relayRead(struct sensor_t *sensor); void relayWrite(struct sensor_t *sensor, bool state, bool send_update=false); void flipRelay(struct sensor_t *sensor, bool send_update=false); void checkButtons(void); void before() { // set relay pins to output mode + restore to last known state for (uint8_t i = 0; i < NUM(sensors); i++) { struct sensor_t *sensor = &sensors[i]; if (sensor->type & SENSOR_RELAY) { pinMode(sensor->relay.pin, OUTPUT); #ifdef SAVE_RESTORE digitalWrite(sensor->relay.pin, loadState(sensor->id) ? RELAY_ON : RELAY_OFF); #else digitalWrite(sensor->relay.pin, RELAY_OFF); #endif } } #ifdef MY_AES_KEY const uint8_t user_aes_key[16] = { MY_AES_KEY }; uint8_t cur_aes_key[16]; hwReadConfigBlock((void*)&cur_aes_key, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, sizeof(cur_aes_key)); if (memcmp(&user_aes_key, &cur_aes_key, 16) != 0) { hwWriteConfigBlock((void*)user_aes_key, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, sizeof(user_aes_key)); debug(PSTR("AES key written\n")); } #endif } void setup() { #ifdef MY_IS_RFM69HW _radio.setHighPower(true); #endif #ifdef MY_RFM69_ATC _radio.enableAutoPower(-70); debug(PSTR("ATC enabled\n")); #endif for (uint8_t i = 0; i < NUM(sensors); i++) { struct sensor_t *sensor = &sensors[i]; if (sensor->type & SENSOR_BUTTON) { pinMode(sensor->button.pin, INPUT_PULLUP); sensor->button.bounce.attach(sensor->button.pin); } } } void presentation() { sendSketchInfo("CouchLight", "1.0"); // register all sensors to gw (they will be created as child devices) for (uint8_t i = 0; i < NUM(sensors); i++) { struct sensor_t *sensor = &sensors[i]; if (sensor->type & SENSOR_RELAY || sensor->type & SENSOR_BUTTON) present(sensor->id, S_BINARY); } #if TEMP_SENSOR_ID >= 0 present(TEMP_SENSOR_ID, S_TEMP); #endif } void loop() { //TODO maybe call _radio.rcCalibration() all 1000x changes? checkButtons(); #if TEMP_SENSOR_ID >= 0 checkTemperature(); #endif } inline void checkTemperature(void) { static unsigned long lastTempUpdate = millis(); static unsigned int numTempUpdates = 0; static float lastTemp = 0; static MyMessage msgTemp(TEMP_SENSOR_ID, V_TEMP); unsigned long now = millis(); if (now - lastTempUpdate > TEMP_READ_INTERVAL) { float temp = _radio.readTemperature() + TEMP_OFFSET; lastTempUpdate = now; if (isnan(temp)) Serial.println(F("Failed reading temperature")); else if (abs(temp - lastTemp) >= 2 || numTempUpdates == TEMP_N_READS_MSG) { lastTemp = temp; numTempUpdates = 0; send(msgTemp.set(temp, 2)); #ifdef MY_DEBUG char str_temp[6]; dtostrf(temp, 4, 2, str_temp); debug(PSTR("Temperature: %s °C\n"), str_temp); #endif } else ++numTempUpdates; } } void receive(const MyMessage &message) { if (message.type == V_STATUS || message.type == V_PERCENTAGE) { uint8_t sensor_id = message.sensor; if (sensor_id >= NUM(sensors)) { Serial.print(F("Invalid sensor id:")); Serial.println(sensor_id); return; } struct sensor_t *sensor = &sensors[sensor_id]; if (message.type == V_STATUS && sensor->type & SENSOR_RELAY) { if (mGetCommand(message) == C_REQ) send(msg.setType(V_STATUS).setSensor(sensor->id).set(relayRead(sensor))); else if (mGetCommand(message) == C_SET) relayWrite(sensor, message.getBool()); } } } bool relayRead(struct sensor_t *sensor) { if (sensor->type & SENSOR_RELAY) return digitalRead(sensor->relay.pin) == RELAY_ON; return false; } void relayWrite(struct sensor_t *sensor, bool state, bool send_update) { if (!(sensor->type & SENSOR_RELAY)) return; Serial.print(F("Incoming change for relay: ")); Serial.print(sensor->relay.pin); Serial.print(F(", New state: ")); Serial.println(state); digitalWrite(sensor->relay.pin, state ? RELAY_ON : RELAY_OFF); #ifdef SAVE_RESTORE saveState(sensor->id, state ? RELAY_ON : RELAY_OFF); #endif if (send_update) send(msg.setType(V_STATUS).setSensor(sensor->id).set(state)); } void flipRelay(struct sensor_t *sensor, bool send_update) { relayWrite(sensor, relayRead(sensor) ? RELAY_OFF : RELAY_ON, send_update); } inline void checkButtons(void) { for (uint8_t i = 0; i < NUM(sensors); i++) { struct sensor_t *sensor = &sensors[i]; if (sensor->type & SENSOR_BUTTON) { sensor->button.bounce.update(); if (sensor->button.bounce.fell()) flipRelay(sensor, true); } } }