/** * 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 2 /** @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; // dimmer pin uint16_t level; // current dim level (0 to 100) } dimmer; }; struct sensor_t sensors[] = { { .id = 0, .type = SENSOR_RELAY, .relay = { .pin = 4 }, }, { .id = 1, .type = SENSOR_RELAY | SENSOR_DIMMER, //.type = SENSOR_RELAY, .relay = { .pin = 5 }, .dimmer = { .pin = 6, .level = 100 }, }, }; //#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 DIMMER_FADE_DELAY 40 // Delay in ms for each percentage fade up/down (10ms = 1s full-range dim) #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); inline uint8_t pwmValue(uint8_t level); void fadeDimmer(struct sensor_t *sensor, uint8_t level, bool send_update=false); 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 } if (sensor->type & SENSOR_DIMMER) { pinMode(sensor->dimmer.pin, OUTPUT); #ifdef SAVE_RESTORE uint8_t level = loadState(NUM(sensors) + sensor->id; #else uint8_t level = sensor->dimmer.level; #endif if ((sensor->type & SENSOR_RELAY) && !relayRead(sensor)) level = 0; analogWrite(sensor->dimmer.pin, pwmValue(level)); } } #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 } void presentation() { sendSketchInfo("TVLight", "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_DIMMER) present(sensor->id, S_DIMMER); else 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? #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()); } else if (message.type == V_PERCENTAGE && sensor->type & SENSOR_DIMMER) { if (mGetCommand(message) == C_REQ) send(msg.setType(V_PERCENTAGE).setSensor(sensor->id).set(sensor->dimmer.level)); else if (mGetCommand(message) == C_SET) { uint16_t level = message.getUInt(); fadeDimmer(sensor, (level > 100) ? 100 : level); } } } } 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); if (sensor->type & SENSOR_DIMMER) analogWrite(sensor->dimmer.pin, state ? pwmValue(sensor->dimmer.level) : 0); #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 uint8_t pwmValue(uint8_t level) { // see https://www.mikrocontroller.net/articles/LED-Fading #if 0 static const uint8_t pwmtable[101] PROGMEM = { 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 8, 8, 8, 9, 9, 10, 11, 11, 12, 12, 13, 14, 15, 16, 16, 17, 18, 19, 20, 22, 23, 24, 25, 27, 28, 30, 32, 33, 35, 37, 39, 42, 44, 47, 49, 52, 55, 58, 61, 65, 68, 72, 76, 81, 85, 90, 95, 100, 106, 112, 118, 125, 132, 139, 147, 156, 164, 174, 183, 194, 205, 216, 228, 241, 255 }; return (uint8_t)pgm_read_byte(&pwmtable[level]); #else // max pwm: 255-19, 101 steps, every step +20 static const uint8_t pwmtable[101] PROGMEM = { 0, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 26, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 32, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 61, 63, 66, 68, 71, 74, 77, 80, 83, 87, 91, 95, 99, 103, 108, 113, 118, 124, 130, 136, 142, 149, 156, 164, 172, 181, 190, 199, 209, 220, 231, 243, 255 }; return 255 - (uint8_t)pgm_read_byte(&pwmtable[level]); #endif } void fadeDimmer(struct sensor_t *sensor, uint8_t level, bool send_update) { if (!(sensor->type & SENSOR_DIMMER)) return; if (level > 100) level = 100; Serial.print(F("Incoming change for dimmer: ")); Serial.print(sensor->dimmer.pin); Serial.print(F(", New level: ")); Serial.println(level); if (level > 0 && sensor->type & SENSOR_RELAY && !relayRead(sensor)) relayWrite(sensor, RELAY_ON); int delta = ((int8_t)(level - sensor->dimmer.level) < 0) ? -1 : 1; while (sensor->dimmer.level != level) { sensor->dimmer.level += delta; analogWrite(sensor->dimmer.pin, pwmValue(sensor->dimmer.level)); delay(DIMMER_FADE_DELAY); } if (level == 0 && sensor->type & SENSOR_RELAY && relayRead(sensor)) relayWrite(sensor, RELAY_OFF); #ifdef SAVE_RESTORE saveState(NUM(sensors) + sensor->id, level); #endif if (send_update) send(msg.setType(V_PERCENTAGE).setSensor(sensor->id).set(level)); }