From e60bc43eb30ab72ec9fdfae581058bf8acfd8586 Mon Sep 17 00:00:00 2001 From: manuel Date: Wed, 5 Feb 2020 13:01:01 +0100 Subject: update to platform IO --- couch_light/.gitignore | 9 +- couch_light/couch_light.ino | 283 --------------------------- couch_light/src/couch_light.cpp | 283 +++++++++++++++++++++++++++ couch_light/src/couch_light.ino | 1 - lr_switch/.gitignore | 5 + lr_switch/lib/readme.txt | 36 ++++ lr_switch/lr_switch.ino | 197 ------------------- lr_switch/platformio.ini | 26 +++ lr_switch/src/lr_switch.cpp | 197 +++++++++++++++++++ oliver/dr_desk/.gitignore | 9 +- oliver/dr_desk/dr_desk.ino | 286 --------------------------- oliver/dr_desk/src/dr_desk.cpp | 287 +++++++++++++++++++++++++++ oliver/dr_desk/src/dr_desk.ino | 1 - oliver/lr_stripes/.gitignore | 9 +- oliver/lr_stripes/lr_stripes.ino | 368 ---------------------------------- oliver/lr_stripes/src/lr_stripes.cpp | 369 +++++++++++++++++++++++++++++++++++ oliver/lr_stripes/src/lr_stripes.ino | 1 - rgbtv_light/.gitignore | 5 + rgbtv_light/lib/readme.txt | 36 ++++ rgbtv_light/platformio.ini | 26 +++ rgbtv_light/rgbtv_light.ino | 241 ----------------------- rgbtv_light/src/rgbtv_light.cpp | 241 +++++++++++++++++++++++ testnode/.gitignore | 5 + testnode/lib/readme.txt | 36 ++++ testnode/platformio.ini | 26 +++ testnode/src/testnode.cpp | 316 ++++++++++++++++++++++++++++++ testnode/testnode.ino | 316 ------------------------------ tv_light/.gitignore | 9 +- tv_light/src/tv_light.cpp | 358 +++++++++++++++++++++++++++++++++ tv_light/src/tv_light.ino | 358 --------------------------------- 30 files changed, 2272 insertions(+), 2068 deletions(-) delete mode 100644 couch_light/couch_light.ino create mode 100644 couch_light/src/couch_light.cpp delete mode 120000 couch_light/src/couch_light.ino create mode 100644 lr_switch/.gitignore create mode 100644 lr_switch/lib/readme.txt delete mode 100644 lr_switch/lr_switch.ino create mode 100644 lr_switch/platformio.ini create mode 100644 lr_switch/src/lr_switch.cpp delete mode 100644 oliver/dr_desk/dr_desk.ino create mode 100644 oliver/dr_desk/src/dr_desk.cpp delete mode 120000 oliver/dr_desk/src/dr_desk.ino delete mode 100644 oliver/lr_stripes/lr_stripes.ino create mode 100644 oliver/lr_stripes/src/lr_stripes.cpp delete mode 120000 oliver/lr_stripes/src/lr_stripes.ino create mode 100644 rgbtv_light/.gitignore create mode 100644 rgbtv_light/lib/readme.txt create mode 100644 rgbtv_light/platformio.ini delete mode 100644 rgbtv_light/rgbtv_light.ino create mode 100644 rgbtv_light/src/rgbtv_light.cpp create mode 100644 testnode/.gitignore create mode 100644 testnode/lib/readme.txt create mode 100644 testnode/platformio.ini create mode 100644 testnode/src/testnode.cpp delete mode 100644 testnode/testnode.ino create mode 100644 tv_light/src/tv_light.cpp delete mode 100644 tv_light/src/tv_light.ino diff --git a/couch_light/.gitignore b/couch_light/.gitignore index e05273b..89cc49c 100644 --- a/couch_light/.gitignore +++ b/couch_light/.gitignore @@ -1,4 +1,5 @@ -.pioenvs -.clang_complete -.gcc-flags.json -.piolibdeps \ No newline at end of file +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/couch_light/couch_light.ino b/couch_light/couch_light.ino deleted file mode 100644 index b99381a..0000000 --- a/couch_light/couch_light.ino +++ /dev/null @@ -1,283 +0,0 @@ -/** - * 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); - } - } -} - diff --git a/couch_light/src/couch_light.cpp b/couch_light/src/couch_light.cpp new file mode 100644 index 0000000..b99381a --- /dev/null +++ b/couch_light/src/couch_light.cpp @@ -0,0 +1,283 @@ +/** + * 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); + } + } +} + diff --git a/couch_light/src/couch_light.ino b/couch_light/src/couch_light.ino deleted file mode 120000 index 230ba97..0000000 --- a/couch_light/src/couch_light.ino +++ /dev/null @@ -1 +0,0 @@ -../couch_light.ino \ No newline at end of file diff --git a/lr_switch/.gitignore b/lr_switch/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/lr_switch/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/lr_switch/lib/readme.txt b/lr_switch/lib/readme.txt new file mode 100644 index 0000000..3f9e008 --- /dev/null +++ b/lr_switch/lib/readme.txt @@ -0,0 +1,36 @@ + +This directory is intended for the project specific (private) libraries. +PlatformIO will compile them to static libraries and link to executable file. + +The source code of each library should be placed in separate directory, like +"lib/private_lib/[here are source files]". + +For example, see how can be organized `Foo` and `Bar` libraries: + +|--lib +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| |--Foo +| | |- Foo.c +| | |- Foo.h +| |- readme.txt --> THIS FILE +|- platformio.ini +|--src + |- main.c + +Then in `src/main.c` you should use: + +#include +#include + +// rest H/C/CPP code + +PlatformIO will find your libraries automatically, configure preprocessor's +include paths and build them. + +More information about PlatformIO Library Dependency Finder +- http://docs.platformio.org/en/stable/librarymanager/ldf.html diff --git a/lr_switch/lr_switch.ino b/lr_switch/lr_switch.ino deleted file mode 100644 index 673eb3a..0000000 --- a/lr_switch/lr_switch.ino +++ /dev/null @@ -1,197 +0,0 @@ -/** - * 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 3 - -/** @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 - -enum sensor_type : uint8_t -{ - SENSOR_RELAY = (1u << 0), - SENSOR_DIMMER = (1u << 1), - SENSOR_BUTTON = (1u << 2), - SENSOR_SCENE = (1u << 3), -}; - -struct sensor_t -{ - uint8_t id; - uint8_t type; - struct - { - uint8_t pin; // push button pin - } button; -}; - -struct sensor_t sensors[] = { - { - .id = 0, - .type = SENSOR_BUTTON | SENSOR_SCENE, - .button = { .pin = 3 }, - }, -}; - -#define NUM(a) (sizeof(a) / sizeof(*a)) - -#define TEMP_SENSOR_ID -1 -#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_SCENE_ON); - -inline void checkTemperature(void); -void wakeUp(void); - -void before() -{ -#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 - //_radio.setPowerLevel(10); -#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); - } -} - -void presentation() -{ - sendSketchInfo("LRSwitch", "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_SCENE) - present(sensor->id, S_SCENE_CONTROLLER); - } - -#if TEMP_SENSOR_ID >= 0 - present(TEMP_SENSOR_ID, S_TEMP); -#endif -} - -void loop() -{ - //TODO maybe call _radio.rcCalibration() all 1000x changes? - - struct sensor_t *sensor = &sensors[0]; - uint8_t pin = sensor->button.pin; - - // delay() instead of sleep() - // sleep() will for whatever reason trigger the external interrupt?! - delay(1000); - - Serial.println("r"); - Serial.flush(); - uint8_t state_before = digitalRead(pin); - int8_t intr = sleep(digitalPinToInterrupt(pin), CHANGE, 0); - if (intr == digitalPinToInterrupt(pin)) - { - uint8_t state_now = digitalRead(pin); - if (state_before != state_now) - { - Serial.println("m"); - send(msg.setType(V_SCENE_ON).setSensor(sensor->id).set(1)); - } - } - -#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; - } -} - diff --git a/lr_switch/platformio.ini b/lr_switch/platformio.ini new file mode 100644 index 0000000..6e0f132 --- /dev/null +++ b/lr_switch/platformio.ini @@ -0,0 +1,26 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter, extra scripting +; Upload options: custom port, speed and extra flags +; Library options: dependencies, extra library storages +; +; Please visit documentation for the other options and examples +; http://docs.platformio.org/en/stable/projectconf.html + +[platformio] +env_default = pro8MHzatmega328 + +[env:miniatmega328] +platform=atmelavr +board=miniatmega328 +framework=arduino + +[env:pro8MHzatmega328] +platform=atmelavr +board=pro8MHzatmega328 +framework=arduino +; minicore/optirun uses 38400 for 8MHz +upload_speed=38400 + +[platformio] +lib_dir=/home/manuel/coding/Arduino/libraries diff --git a/lr_switch/src/lr_switch.cpp b/lr_switch/src/lr_switch.cpp new file mode 100644 index 0000000..673eb3a --- /dev/null +++ b/lr_switch/src/lr_switch.cpp @@ -0,0 +1,197 @@ +/** + * 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 3 + +/** @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 + +enum sensor_type : uint8_t +{ + SENSOR_RELAY = (1u << 0), + SENSOR_DIMMER = (1u << 1), + SENSOR_BUTTON = (1u << 2), + SENSOR_SCENE = (1u << 3), +}; + +struct sensor_t +{ + uint8_t id; + uint8_t type; + struct + { + uint8_t pin; // push button pin + } button; +}; + +struct sensor_t sensors[] = { + { + .id = 0, + .type = SENSOR_BUTTON | SENSOR_SCENE, + .button = { .pin = 3 }, + }, +}; + +#define NUM(a) (sizeof(a) / sizeof(*a)) + +#define TEMP_SENSOR_ID -1 +#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_SCENE_ON); + +inline void checkTemperature(void); +void wakeUp(void); + +void before() +{ +#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 + //_radio.setPowerLevel(10); +#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); + } +} + +void presentation() +{ + sendSketchInfo("LRSwitch", "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_SCENE) + present(sensor->id, S_SCENE_CONTROLLER); + } + +#if TEMP_SENSOR_ID >= 0 + present(TEMP_SENSOR_ID, S_TEMP); +#endif +} + +void loop() +{ + //TODO maybe call _radio.rcCalibration() all 1000x changes? + + struct sensor_t *sensor = &sensors[0]; + uint8_t pin = sensor->button.pin; + + // delay() instead of sleep() + // sleep() will for whatever reason trigger the external interrupt?! + delay(1000); + + Serial.println("r"); + Serial.flush(); + uint8_t state_before = digitalRead(pin); + int8_t intr = sleep(digitalPinToInterrupt(pin), CHANGE, 0); + if (intr == digitalPinToInterrupt(pin)) + { + uint8_t state_now = digitalRead(pin); + if (state_before != state_now) + { + Serial.println("m"); + send(msg.setType(V_SCENE_ON).setSensor(sensor->id).set(1)); + } + } + +#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; + } +} + diff --git a/oliver/dr_desk/.gitignore b/oliver/dr_desk/.gitignore index 5dac9f5..89cc49c 100644 --- a/oliver/dr_desk/.gitignore +++ b/oliver/dr_desk/.gitignore @@ -1,4 +1,5 @@ -.pioenvs -.piolibdeps -.clang_complete -.gcc-flags.json +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/oliver/dr_desk/dr_desk.ino b/oliver/dr_desk/dr_desk.ino deleted file mode 100644 index 0ff13e2..0000000 --- a/oliver/dr_desk/dr_desk.ino +++ /dev/null @@ -1,286 +0,0 @@ -#include -#include -#include -#include -#include -#include "secrets.h" - -const char* autoconf_ssid = AUTOCONF_SSID; //AP name for WiFi setup AP which your ESP will open when not able to connect to other WiFi -const char* autoconf_pwd = AUTOCONF_PASSWORD; // AP password so noone else can connect to the ESP in case your router fails -const char* mqtt_server = "192.168.1.2"; //MQTT Server IP, your home MQTT server eg Mosquitto on RPi, or some public MQTT -const int mqtt_port = 1883; //MQTT Server PORT, default is 1883 but can be anything. - -const char* mqtt_pingall_sub = "home/pingall"; -const char* mqtt_pingall_pub = "home/pingall/response"; - -#define MQTT_BASE "home/diningroom/desk" -const char* mqtt_device_boot = MQTT_BASE "/device"; - -const char* mqtt_mode_sub = MQTT_BASE; -const char* mqtt_mode_pub = MQTT_BASE "/status"; - -const char* mqtt_color_sub = MQTT_BASE "/color"; -const char* mqtt_color_pub = MQTT_BASE "/color/status"; - -const char* mqtt_brightness_sub = MQTT_BASE "/brightness"; -const char* mqtt_brightness_pub = MQTT_BASE "/brightness/status"; - -#define RGB_PIN 2 -#define RGB_NUM_LEDS 12 -#define RGB_CHIPSET WS2812B -#define RGB_COLOR_ORDER GRB -#define NUM(a) (sizeof(a) / sizeof(*a)) - -WiFiClient espClient; -PubSubClient client(espClient); -char convBuffer[10]; -CRGB leds[RGB_NUM_LEDS]; - -void switchMode(struct mode *mode); -void modeOff(); -void modeSolid(); -void modeRainbow(); -void modeRainbowFast(); -void modeStrobo(); - -struct mode { - const char *name; - void (*func)(); -}; - -struct mode modes[] = { - { "off", modeOff }, - { "solid", modeSolid }, - { "rainbow", modeRainbow }, - { "rainbowfast", modeRainbowFast }, - { "strobo", modeStrobo }, -}; - -struct -{ - struct mode *mode = &modes[0]; - uint32_t color = CRGB::Red; - uint8_t brightness = 204; // 80% -} set; - -struct -{ - uint8_t brightness; - bool idle = false; -} current; - -void setup() -{ - Serial.begin(115200); - pinMode(BUILTIN_LED, OUTPUT); - - FastLED.addLeds(leds, RGB_NUM_LEDS); - current.brightness = set.brightness; - // update leds - set.mode->func(); - FastLED.show(); - - WiFiManager wifiManager; - wifiManager.autoConnect(autoconf_ssid, autoconf_pwd); - - setup_ota(); - - client.setServer(mqtt_server, mqtt_port); - client.setCallback(callback); - - digitalWrite(BUILTIN_LED, HIGH); //Turn off led as default -} - -void setup_ota() -{ - // Set OTA Password, and change it in platformio.ini - ArduinoOTA.setPassword(OTA_PASSWORD); - ArduinoOTA.onStart([]() {}); - ArduinoOTA.onEnd([]() {}); - ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {}); - ArduinoOTA.onError([](ota_error_t error) - { - if (error == OTA_AUTH_ERROR); // Auth failed - else if (error == OTA_BEGIN_ERROR); // Begin failed - else if (error == OTA_CONNECT_ERROR); // Connect failed - else if (error == OTA_RECEIVE_ERROR); // Receive failed - else if (error == OTA_END_ERROR); // End failed - }); - ArduinoOTA.begin(); -} - -void reconnect() -{ - // Loop until we're reconnected - while (!client.connected()) - { - // Create a random client ID - String clientId = "ESP8266Client-"; - clientId += String(random(0xffff), HEX); - - // Attempt to connect - if (client.connect(clientId.c_str())) - { - // Once connected, publish an announcement... - client.publish(mqtt_device_boot, "connected"); - // ... and resubscribe - client.subscribe(mqtt_pingall_sub); - - // publish states - Serial.println(mqtt_mode_sub); - client.subscribe(mqtt_mode_sub); - client.publish(mqtt_mode_pub, set.mode->name, true); - - Serial.println(mqtt_color_sub); - client.subscribe(mqtt_color_sub); - itoa(set.color, convBuffer, 10); - client.publish(mqtt_color_pub, convBuffer, true); - - Serial.println(mqtt_brightness_sub); - client.subscribe(mqtt_brightness_sub); - itoa(map(set.brightness, 0, 255, 0, 100), convBuffer, 10); - client.publish(mqtt_brightness_pub, convBuffer, true); - } - else - { - // Wait 5 seconds before retrying - delay(5000); - } - } -} - -void callback(char *topic, uint8_t *payload, unsigned int length) -{ - uint8_t c_payload[length]; - memcpy(c_payload, payload, length); - c_payload[length] = '\0'; - - if (strcmp(topic, mqtt_pingall_sub) == 0) - { - blink(); - client.publish(mqtt_pingall_pub, - "{\"diningroom_desk\":\"connected\"}"); - } - else if (strcmp(topic, mqtt_mode_sub) == 0) - { - for (uint8_t i = 0; i < NUM(modes); ++i) - { - if (strcmp(modes[i].name, (char *)c_payload) != 0) - continue; - switchMode(&modes[i]); - break; - } - } - else if (strcmp(topic, mqtt_color_sub) == 0) - { - // switch from OFF to SOLID - if (set.mode == &modes[0]) - switchMode(&modes[1]); - - set.color = atoi((char *)c_payload); - current.idle = false; - itoa(set.color, convBuffer, 10); - client.publish(mqtt_color_pub, convBuffer, true); - } - else if (strcmp(topic, mqtt_brightness_sub) == 0) - { - set.brightness = map(atoi((char *)c_payload), 0, 100, 0, 255); - current.idle = false; - itoa(map(set.brightness, 0, 255, 0, 100), convBuffer, 10); - client.publish(mqtt_brightness_pub, convBuffer, true); - } -} - -void blink() -{ - //Blink on received MQTT message - digitalWrite(BUILTIN_LED, LOW); - delay(25); - digitalWrite(BUILTIN_LED, HIGH); -} - -void calcBrightness() -{ - #define FADE_STEP 10 - if (current.brightness == set.brightness) - return; - int fadeAmount = set.brightness - current.brightness; - if (abs(fadeAmount) > FADE_STEP) - fadeAmount = (fadeAmount > 0) ? FADE_STEP : -FADE_STEP; - current.brightness += fadeAmount; -} - -void switchMode(struct mode *mode) -{ - if (set.mode == mode) - return; - Serial.print("Switching mode to "); - Serial.println(mode->name); - set.mode = mode; - current.idle = false; - client.publish(mqtt_mode_pub, set.mode->name, true); -} - -void modeOff() -{ - fill_solid(leds, RGB_NUM_LEDS, CRGB::Black); - current.idle = true; -} - -void modeSolid() -{ - fill_solid(leds, RGB_NUM_LEDS, set.color); - calcBrightness(); - nscale8_video(leds, RGB_NUM_LEDS, current.brightness); - current.idle = (current.brightness == set.brightness); -} - -void modeRainbow() -{ - static CRGBPalette16 palette = RainbowColors_p; - static uint8_t hue = 0; - calcBrightness(); - for (int i = 0; i < RGB_NUM_LEDS; ++i) - leds[i] = ColorFromPalette(palette, hue, current.brightness); - hue++; -} - -void modeRainbowFast() -{ - static CRGBPalette16 palette = RainbowColors_p; - static uint8_t hue = 0; - calcBrightness(); - for (int i = 0; i < RGB_NUM_LEDS; ++i) - leds[i] = ColorFromPalette(palette, hue, current.brightness); - hue+=5; -} - -void modeStrobo() -{ - static bool state = 0; - if (set.color == CRGB::Black) - return; - fill_solid(leds, RGB_NUM_LEDS, (state) ? set.color : CRGB::Black); - calcBrightness(); - nscale8_video(leds, RGB_NUM_LEDS, current.brightness); - state = !state; -} - -void loop() -{ - if (!client.connected()) - reconnect(); - client.loop(); - ArduinoOTA.handle(); - - EVERY_N_MILLISECONDS(100) - { - if (!current.idle) - { - set.mode->func(); - FastLED.show(); - } - if (!current.idle && current.brightness == 0) - current.idle = true; - } -} diff --git a/oliver/dr_desk/src/dr_desk.cpp b/oliver/dr_desk/src/dr_desk.cpp new file mode 100644 index 0000000..46bf8eb --- /dev/null +++ b/oliver/dr_desk/src/dr_desk.cpp @@ -0,0 +1,287 @@ +#include +#include +#include +#include +#include +#include +#include "secrets.h" + +const char* autoconf_ssid = AUTOCONF_SSID; //AP name for WiFi setup AP which your ESP will open when not able to connect to other WiFi +const char* autoconf_pwd = AUTOCONF_PASSWORD; // AP password so noone else can connect to the ESP in case your router fails +const char* mqtt_server = "192.168.1.2"; //MQTT Server IP, your home MQTT server eg Mosquitto on RPi, or some public MQTT +const int mqtt_port = 1883; //MQTT Server PORT, default is 1883 but can be anything. + +const char* mqtt_pingall_sub = "home/pingall"; +const char* mqtt_pingall_pub = "home/pingall/response"; + +#define MQTT_BASE "home/diningroom/desk" +const char* mqtt_device_boot = MQTT_BASE "/device"; + +const char* mqtt_mode_sub = MQTT_BASE; +const char* mqtt_mode_pub = MQTT_BASE "/status"; + +const char* mqtt_color_sub = MQTT_BASE "/color"; +const char* mqtt_color_pub = MQTT_BASE "/color/status"; + +const char* mqtt_brightness_sub = MQTT_BASE "/brightness"; +const char* mqtt_brightness_pub = MQTT_BASE "/brightness/status"; + +#define RGB_PIN 2 +#define RGB_NUM_LEDS 12 +#define RGB_CHIPSET WS2812B +#define RGB_COLOR_ORDER GRB +#define NUM(a) (sizeof(a) / sizeof(*a)) + +WiFiClient espClient; +PubSubClient client(espClient); +char convBuffer[10]; +CRGB leds[RGB_NUM_LEDS]; + +void switchMode(struct mode *mode); +void modeOff(); +void modeSolid(); +void modeRainbow(); +void modeRainbowFast(); +void modeStrobo(); + +struct mode { + const char *name; + void (*func)(); +}; + +struct mode modes[] = { + { "off", modeOff }, + { "solid", modeSolid }, + { "rainbow", modeRainbow }, + { "rainbowfast", modeRainbowFast }, + { "strobo", modeStrobo }, +}; + +struct +{ + struct mode *mode = &modes[0]; + uint32_t color = CRGB::Red; + uint8_t brightness = 204; // 80% +} set; + +struct +{ + uint8_t brightness; + bool idle = false; +} current; + +void setup() +{ + Serial.begin(115200); + pinMode(BUILTIN_LED, OUTPUT); + + FastLED.addLeds(leds, RGB_NUM_LEDS); + current.brightness = set.brightness; + // update leds + set.mode->func(); + FastLED.show(); + + WiFiManager wifiManager; + wifiManager.autoConnect(autoconf_ssid, autoconf_pwd); + + setup_ota(); + + client.setServer(mqtt_server, mqtt_port); + client.setCallback(callback); + + digitalWrite(BUILTIN_LED, HIGH); //Turn off led as default +} + +void setup_ota() +{ + // Set OTA Password, and change it in platformio.ini + ArduinoOTA.setPassword(OTA_PASSWORD); + ArduinoOTA.onStart([]() {}); + ArduinoOTA.onEnd([]() {}); + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {}); + ArduinoOTA.onError([](ota_error_t error) + { + if (error == OTA_AUTH_ERROR); // Auth failed + else if (error == OTA_BEGIN_ERROR); // Begin failed + else if (error == OTA_CONNECT_ERROR); // Connect failed + else if (error == OTA_RECEIVE_ERROR); // Receive failed + else if (error == OTA_END_ERROR); // End failed + }); + ArduinoOTA.begin(); +} + +void reconnect() +{ + // Loop until we're reconnected + while (!client.connected()) + { + // Create a random client ID + String clientId = "ESP8266Client-"; + clientId += String(random(0xffff), HEX); + + // Attempt to connect + if (client.connect(clientId.c_str())) + { + // Once connected, publish an announcement... + client.publish(mqtt_device_boot, "connected"); + // ... and resubscribe + client.subscribe(mqtt_pingall_sub); + + // publish states + Serial.println(mqtt_mode_sub); + client.subscribe(mqtt_mode_sub); + client.publish(mqtt_mode_pub, set.mode->name, true); + + Serial.println(mqtt_color_sub); + client.subscribe(mqtt_color_sub); + itoa(set.color, convBuffer, 10); + client.publish(mqtt_color_pub, convBuffer, true); + + Serial.println(mqtt_brightness_sub); + client.subscribe(mqtt_brightness_sub); + itoa(map(set.brightness, 0, 255, 0, 100), convBuffer, 10); + client.publish(mqtt_brightness_pub, convBuffer, true); + } + else + { + // Wait 5 seconds before retrying + delay(5000); + } + } +} + +void callback(char *topic, uint8_t *payload, unsigned int length) +{ + uint8_t c_payload[length]; + memcpy(c_payload, payload, length); + c_payload[length] = '\0'; + + if (strcmp(topic, mqtt_pingall_sub) == 0) + { + blink(); + client.publish(mqtt_pingall_pub, + "{\"diningroom_desk\":\"connected\"}"); + } + else if (strcmp(topic, mqtt_mode_sub) == 0) + { + for (uint8_t i = 0; i < NUM(modes); ++i) + { + if (strcmp(modes[i].name, (char *)c_payload) != 0) + continue; + switchMode(&modes[i]); + break; + } + } + else if (strcmp(topic, mqtt_color_sub) == 0) + { + // switch from OFF to SOLID + if (set.mode == &modes[0]) + switchMode(&modes[1]); + + set.color = atoi((char *)c_payload); + current.idle = false; + itoa(set.color, convBuffer, 10); + client.publish(mqtt_color_pub, convBuffer, true); + } + else if (strcmp(topic, mqtt_brightness_sub) == 0) + { + set.brightness = map(atoi((char *)c_payload), 0, 100, 0, 255); + current.idle = false; + itoa(map(set.brightness, 0, 255, 0, 100), convBuffer, 10); + client.publish(mqtt_brightness_pub, convBuffer, true); + } +} + +void blink() +{ + //Blink on received MQTT message + digitalWrite(BUILTIN_LED, LOW); + delay(25); + digitalWrite(BUILTIN_LED, HIGH); +} + +void calcBrightness() +{ + #define FADE_STEP 10 + if (current.brightness == set.brightness) + return; + int fadeAmount = set.brightness - current.brightness; + if (abs(fadeAmount) > FADE_STEP) + fadeAmount = (fadeAmount > 0) ? FADE_STEP : -FADE_STEP; + current.brightness += fadeAmount; +} + +void switchMode(struct mode *mode) +{ + if (set.mode == mode) + return; + Serial.print("Switching mode to "); + Serial.println(mode->name); + set.mode = mode; + current.idle = false; + client.publish(mqtt_mode_pub, set.mode->name, true); +} + +void modeOff() +{ + fill_solid(leds, RGB_NUM_LEDS, CRGB::Black); + current.idle = true; +} + +void modeSolid() +{ + fill_solid(leds, RGB_NUM_LEDS, set.color); + calcBrightness(); + nscale8_video(leds, RGB_NUM_LEDS, current.brightness); + current.idle = (current.brightness == set.brightness); +} + +void modeRainbow() +{ + static CRGBPalette16 palette = RainbowColors_p; + static uint8_t hue = 0; + calcBrightness(); + for (int i = 0; i < RGB_NUM_LEDS; ++i) + leds[i] = ColorFromPalette(palette, hue, current.brightness); + hue++; +} + +void modeRainbowFast() +{ + static CRGBPalette16 palette = RainbowColors_p; + static uint8_t hue = 0; + calcBrightness(); + for (int i = 0; i < RGB_NUM_LEDS; ++i) + leds[i] = ColorFromPalette(palette, hue, current.brightness); + hue+=5; +} + +void modeStrobo() +{ + static bool state = 0; + if (set.color == CRGB::Black) + return; + fill_solid(leds, RGB_NUM_LEDS, (state) ? set.color : CRGB::Black); + calcBrightness(); + nscale8_video(leds, RGB_NUM_LEDS, current.brightness); + state = !state; +} + +void loop() +{ + if (!client.connected()) + reconnect(); + client.loop(); + ArduinoOTA.handle(); + + EVERY_N_MILLISECONDS(100) + { + if (!current.idle) + { + set.mode->func(); + FastLED.show(); + } + if (!current.idle && current.brightness == 0) + current.idle = true; + } +} diff --git a/oliver/dr_desk/src/dr_desk.ino b/oliver/dr_desk/src/dr_desk.ino deleted file mode 120000 index 0cdf13b..0000000 --- a/oliver/dr_desk/src/dr_desk.ino +++ /dev/null @@ -1 +0,0 @@ -../dr_desk.ino \ No newline at end of file diff --git a/oliver/lr_stripes/.gitignore b/oliver/lr_stripes/.gitignore index 5dac9f5..89cc49c 100644 --- a/oliver/lr_stripes/.gitignore +++ b/oliver/lr_stripes/.gitignore @@ -1,4 +1,5 @@ -.pioenvs -.piolibdeps -.clang_complete -.gcc-flags.json +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/oliver/lr_stripes/lr_stripes.ino b/oliver/lr_stripes/lr_stripes.ino deleted file mode 100644 index 2cd63d4..0000000 --- a/oliver/lr_stripes/lr_stripes.ino +++ /dev/null @@ -1,368 +0,0 @@ -#include -#include -#include -#include -#include -#include "secrets.h" - -const char* autoconf_ssid = AUTOCONF_SSID; //AP name for WiFi setup AP which your ESP will open when not able to connect to other WiFi -const char* autoconf_pwd = AUTOCONF_PASSWORD; // AP password so noone else can connect to the ESP in case your router fails -const char* mqtt_server = "192.168.1.2"; //MQTT Server IP, your home MQTT server eg Mosquitto on RPi, or some public MQTT -const int mqtt_port = 1883; //MQTT Server PORT, default is 1883 but can be anything. - -const char* mqtt_pingall_sub = "home/pingall"; -const char* mqtt_pingall_pub = "home/pingall/response"; - -#define MQTT_BASE "home/livingroom/ledstripe" -const char* mqtt_device_boot = MQTT_BASE "/device"; - -enum sensor_type : uint8_t -{ - SENSOR_RELAY = (1u << 0), - SENSOR_DIMMER = (1u << 1), - SENSOR_BUTTON = (1u << 2), -}; - -struct sensor_t -{ - uint8_t type; - struct - { - uint8_t pin; // relay pin - const char *mqtt_sub; - const char *mqtt_pub; - } relay; - struct - { - uint8_t pin; // push button pin - Bounce bounce; - } button; - struct - { - uint8_t pin; // dimmer pin - uint8_t level; // current dim level (0 to 100) - const char *mqtt_sub; - const char *mqtt_pub; - } dimmer; -}; - -struct sensor_t sensors[] = { - { - .type = SENSOR_RELAY | SENSOR_BUTTON | SENSOR_DIMMER, - .relay = { - .pin = D5, - .mqtt_sub = MQTT_BASE, - .mqtt_pub = MQTT_BASE "/status" - }, - .button = { - .pin = D0, - .bounce = Bounce(), - }, - .dimmer = { - .pin = D8, - .level = 100, - .mqtt_sub = MQTT_BASE "/brightness", - .mqtt_pub = MQTT_BASE "/brightness/status" - }, - } -}; - -//#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) - -WiFiClient espClient; -PubSubClient client(espClient); -char convBuffer[10]; - -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); -inline uint8_t pwmValue(uint8_t level); -void fadeDimmer(struct sensor_t *sensor, uint8_t level, bool send_update=false); - -void setup() -{ - Serial.begin(115200); - pinMode(BUILTIN_LED, OUTPUT); - analogWriteRange(100); - - // 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 -#error "not implemented" -#else - digitalWrite(sensor->relay.pin, RELAY_OFF); -#endif - } - - if (sensor->type & SENSOR_DIMMER) - { - pinMode(sensor->dimmer.pin, OUTPUT); -#ifdef SAVE_RESTORE -#error "not implemented" -#else - uint8_t level = sensor->dimmer.level; -#endif - if ((sensor->type & SENSOR_RELAY) && !relayRead(sensor)) - level = 0; - analogWrite(sensor->dimmer.pin, pwmValue(level)); - } - - if (sensor->type & SENSOR_BUTTON) - { - pinMode(sensor->button.pin, INPUT); - sensor->button.bounce.attach(sensor->button.pin); - } - } - - WiFiManager wifiManager; - wifiManager.autoConnect(autoconf_ssid, autoconf_pwd); - - setup_ota(); - - client.setServer(mqtt_server, mqtt_port); - client.setCallback(callback); - - digitalWrite(BUILTIN_LED, HIGH); //Turn off led as default -} - -void setup_ota() -{ - // Set OTA Password, and change it in platformio.ini - ArduinoOTA.setPassword(OTA_PASSWORD); - ArduinoOTA.onStart([]() {}); - ArduinoOTA.onEnd([]() {}); - ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {}); - ArduinoOTA.onError([](ota_error_t error) - { - if (error == OTA_AUTH_ERROR); // Auth failed - else if (error == OTA_BEGIN_ERROR); // Begin failed - else if (error == OTA_CONNECT_ERROR); // Connect failed - else if (error == OTA_RECEIVE_ERROR); // Receive failed - else if (error == OTA_END_ERROR); // End failed - }); - ArduinoOTA.begin(); -} - -void reconnect() -{ - // Loop until we're reconnected - while (!client.connected()) - { - // Create a random client ID - String clientId = "ESP8266Client-"; - clientId += String(random(0xffff), HEX); - - // Attempt to connect - if (client.connect(clientId.c_str())) - { - // Once connected, publish an announcement... - client.publish(mqtt_device_boot, "connected"); - // ... and resubscribe - client.subscribe(mqtt_pingall_sub); - - // publish states - for (uint8_t i = 0; i < NUM(sensors); i++) - { - struct sensor_t *sensor = &sensors[i]; - if (sensor->type & SENSOR_RELAY) - { - Serial.println(sensor->relay.mqtt_sub); - client.subscribe(sensor->relay.mqtt_sub); - client.publish(sensor->relay.mqtt_pub, relayRead(sensor) ? "1" : "0", - true); - } - - if (sensor->type & SENSOR_DIMMER) - { - Serial.println(sensor->dimmer.mqtt_sub); - client.subscribe(sensor->dimmer.mqtt_sub); - itoa(sensor->dimmer.level, convBuffer, 10); - client.publish(sensor->dimmer.mqtt_pub, convBuffer, true); - } - } - } - else - { - // Wait 5 seconds before retrying - delay(5000); - } - } -} - -void callback(char* topic, byte* payload, unsigned int length) -{ - char c_payload[length]; - memcpy(c_payload, payload, length); - c_payload[length] = '\0'; - - if (strcmp(topic, mqtt_pingall_sub) == 0) - { - blink(); - client.publish(mqtt_pingall_pub, - "{\"livingroom_ledstrip\":\"connected\"}"); - return; - } - - for (uint8_t i = 0; i < NUM(sensors); i++) - { - struct sensor_t *sensor = &sensors[i]; - if (sensor->type & SENSOR_RELAY - && length > 0 - && strcmp(topic, sensor->relay.mqtt_sub) == 0) - { - blink(); - relayWrite(sensor, payload[0] == '1', true); - return; - } - - if (sensor->type & SENSOR_DIMMER - && length > 0 - && strcmp(topic, sensor->dimmer.mqtt_sub) == 0) - { - blink(); - uint8_t level = atoi((char *)payload); - fadeDimmer(sensor, (level > 100) ? 100 : level, true); - return; - } - } -} - -void blink() -{ - //Blink on received MQTT message - digitalWrite(BUILTIN_LED, LOW); - delay(25); - digitalWrite(BUILTIN_LED, HIGH); -} - -void loop() -{ - if (!client.connected()) - reconnect(); - client.loop(); - ArduinoOTA.handle(); - checkButtons(); -} - -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 -#error "not implemented" -#endif - - if (send_update) - client.publish(sensor->relay.mqtt_pub, state ? "1" : "0", true); -} - -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); - } - } -} - -const uint8_t pwmtable[101] PROGMEM = { - // value below 0 turns of the power supply - 0, 10, 10, 11, 11, 11, 11, 12, 12, 12, - 13, 13, 13, 13, 14, 14, 14, 15, 15, 15, - 16, 16, 17, 17, 17, 18, 18, 19, 19, 19, - 20, 20, 21, 21, 22, 22, 23, 23, 24, 25, - 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, - 32, 32, 33, 34, 35, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 54, 55, 56, 58, 59, 60, 62, - 63, 65, 66, 68, 69, 71, 72, 74, 76, 78, - 79, 81, 83, 85, 87, 89, 91, 93, 95, 98, - 100 -}; - -inline uint8_t pwmValue(uint8_t level) -{ - //uint8_t lvl = 100 - (uint8_t)pgm_read_byte(&pwmtable[level]); - //return (lvl < 10) 10 : lvl; - //level = (level > 0 && level < 10) ? 10 : level; - //return 100 - level; - return 100 - (uint8_t)pgm_read_byte(&pwmtable[level]); -} - -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, send_update); - - 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, send_update); - -#ifdef SAVE_RESTORE -#error "not implemented" -#endif - - if (send_update) - { - itoa(level, convBuffer, 10); - client.publish(sensor->dimmer.mqtt_pub, convBuffer, true); - } -} diff --git a/oliver/lr_stripes/src/lr_stripes.cpp b/oliver/lr_stripes/src/lr_stripes.cpp new file mode 100644 index 0000000..7059f8b --- /dev/null +++ b/oliver/lr_stripes/src/lr_stripes.cpp @@ -0,0 +1,369 @@ +#include +#include +#include +#include +#include +#include +#include "secrets.h" + +const char* autoconf_ssid = AUTOCONF_SSID; //AP name for WiFi setup AP which your ESP will open when not able to connect to other WiFi +const char* autoconf_pwd = AUTOCONF_PASSWORD; // AP password so noone else can connect to the ESP in case your router fails +const char* mqtt_server = "192.168.1.2"; //MQTT Server IP, your home MQTT server eg Mosquitto on RPi, or some public MQTT +const int mqtt_port = 1883; //MQTT Server PORT, default is 1883 but can be anything. + +const char* mqtt_pingall_sub = "home/pingall"; +const char* mqtt_pingall_pub = "home/pingall/response"; + +#define MQTT_BASE "home/livingroom/ledstripe" +const char* mqtt_device_boot = MQTT_BASE "/device"; + +enum sensor_type : uint8_t +{ + SENSOR_RELAY = (1u << 0), + SENSOR_DIMMER = (1u << 1), + SENSOR_BUTTON = (1u << 2), +}; + +struct sensor_t +{ + uint8_t type; + struct + { + uint8_t pin; // relay pin + const char *mqtt_sub; + const char *mqtt_pub; + } relay; + struct + { + uint8_t pin; // push button pin + Bounce bounce; + } button; + struct + { + uint8_t pin; // dimmer pin + uint8_t level; // current dim level (0 to 100) + const char *mqtt_sub; + const char *mqtt_pub; + } dimmer; +}; + +struct sensor_t sensors[] = { + { + .type = SENSOR_RELAY | SENSOR_BUTTON | SENSOR_DIMMER, + .relay = { + .pin = D5, + .mqtt_sub = MQTT_BASE, + .mqtt_pub = MQTT_BASE "/status" + }, + .button = { + .pin = D0, + .bounce = Bounce(), + }, + .dimmer = { + .pin = D8, + .level = 100, + .mqtt_sub = MQTT_BASE "/brightness", + .mqtt_pub = MQTT_BASE "/brightness/status" + }, + } +}; + +//#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) + +WiFiClient espClient; +PubSubClient client(espClient); +char convBuffer[10]; + +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); +inline uint8_t pwmValue(uint8_t level); +void fadeDimmer(struct sensor_t *sensor, uint8_t level, bool send_update=false); + +void setup() +{ + Serial.begin(115200); + pinMode(BUILTIN_LED, OUTPUT); + analogWriteRange(100); + + // 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 +#error "not implemented" +#else + digitalWrite(sensor->relay.pin, RELAY_OFF); +#endif + } + + if (sensor->type & SENSOR_DIMMER) + { + pinMode(sensor->dimmer.pin, OUTPUT); +#ifdef SAVE_RESTORE +#error "not implemented" +#else + uint8_t level = sensor->dimmer.level; +#endif + if ((sensor->type & SENSOR_RELAY) && !relayRead(sensor)) + level = 0; + analogWrite(sensor->dimmer.pin, pwmValue(level)); + } + + if (sensor->type & SENSOR_BUTTON) + { + pinMode(sensor->button.pin, INPUT); + sensor->button.bounce.attach(sensor->button.pin); + } + } + + WiFiManager wifiManager; + wifiManager.autoConnect(autoconf_ssid, autoconf_pwd); + + setup_ota(); + + client.setServer(mqtt_server, mqtt_port); + client.setCallback(callback); + + digitalWrite(BUILTIN_LED, HIGH); //Turn off led as default +} + +void setup_ota() +{ + // Set OTA Password, and change it in platformio.ini + ArduinoOTA.setPassword(OTA_PASSWORD); + ArduinoOTA.onStart([]() {}); + ArduinoOTA.onEnd([]() {}); + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {}); + ArduinoOTA.onError([](ota_error_t error) + { + if (error == OTA_AUTH_ERROR); // Auth failed + else if (error == OTA_BEGIN_ERROR); // Begin failed + else if (error == OTA_CONNECT_ERROR); // Connect failed + else if (error == OTA_RECEIVE_ERROR); // Receive failed + else if (error == OTA_END_ERROR); // End failed + }); + ArduinoOTA.begin(); +} + +void reconnect() +{ + // Loop until we're reconnected + while (!client.connected()) + { + // Create a random client ID + String clientId = "ESP8266Client-"; + clientId += String(random(0xffff), HEX); + + // Attempt to connect + if (client.connect(clientId.c_str())) + { + // Once connected, publish an announcement... + client.publish(mqtt_device_boot, "connected"); + // ... and resubscribe + client.subscribe(mqtt_pingall_sub); + + // publish states + for (uint8_t i = 0; i < NUM(sensors); i++) + { + struct sensor_t *sensor = &sensors[i]; + if (sensor->type & SENSOR_RELAY) + { + Serial.println(sensor->relay.mqtt_sub); + client.subscribe(sensor->relay.mqtt_sub); + client.publish(sensor->relay.mqtt_pub, relayRead(sensor) ? "1" : "0", + true); + } + + if (sensor->type & SENSOR_DIMMER) + { + Serial.println(sensor->dimmer.mqtt_sub); + client.subscribe(sensor->dimmer.mqtt_sub); + itoa(sensor->dimmer.level, convBuffer, 10); + client.publish(sensor->dimmer.mqtt_pub, convBuffer, true); + } + } + } + else + { + // Wait 5 seconds before retrying + delay(5000); + } + } +} + +void callback(char* topic, byte* payload, unsigned int length) +{ + char c_payload[length]; + memcpy(c_payload, payload, length); + c_payload[length] = '\0'; + + if (strcmp(topic, mqtt_pingall_sub) == 0) + { + blink(); + client.publish(mqtt_pingall_pub, + "{\"livingroom_ledstrip\":\"connected\"}"); + return; + } + + for (uint8_t i = 0; i < NUM(sensors); i++) + { + struct sensor_t *sensor = &sensors[i]; + if (sensor->type & SENSOR_RELAY + && length > 0 + && strcmp(topic, sensor->relay.mqtt_sub) == 0) + { + blink(); + relayWrite(sensor, payload[0] == '1', true); + return; + } + + if (sensor->type & SENSOR_DIMMER + && length > 0 + && strcmp(topic, sensor->dimmer.mqtt_sub) == 0) + { + blink(); + uint8_t level = atoi((char *)payload); + fadeDimmer(sensor, (level > 100) ? 100 : level, true); + return; + } + } +} + +void blink() +{ + //Blink on received MQTT message + digitalWrite(BUILTIN_LED, LOW); + delay(25); + digitalWrite(BUILTIN_LED, HIGH); +} + +void loop() +{ + if (!client.connected()) + reconnect(); + client.loop(); + ArduinoOTA.handle(); + checkButtons(); +} + +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 +#error "not implemented" +#endif + + if (send_update) + client.publish(sensor->relay.mqtt_pub, state ? "1" : "0", true); +} + +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); + } + } +} + +const uint8_t pwmtable[101] PROGMEM = { + // value below 0 turns of the power supply + 0, 10, 10, 11, 11, 11, 11, 12, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 15, 15, 15, + 16, 16, 17, 17, 17, 18, 18, 19, 19, 19, + 20, 20, 21, 21, 22, 22, 23, 23, 24, 25, + 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, + 32, 32, 33, 34, 35, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 54, 55, 56, 58, 59, 60, 62, + 63, 65, 66, 68, 69, 71, 72, 74, 76, 78, + 79, 81, 83, 85, 87, 89, 91, 93, 95, 98, + 100 +}; + +inline uint8_t pwmValue(uint8_t level) +{ + //uint8_t lvl = 100 - (uint8_t)pgm_read_byte(&pwmtable[level]); + //return (lvl < 10) 10 : lvl; + //level = (level > 0 && level < 10) ? 10 : level; + //return 100 - level; + return 100 - (uint8_t)pgm_read_byte(&pwmtable[level]); +} + +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, send_update); + + 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, send_update); + +#ifdef SAVE_RESTORE +#error "not implemented" +#endif + + if (send_update) + { + itoa(level, convBuffer, 10); + client.publish(sensor->dimmer.mqtt_pub, convBuffer, true); + } +} diff --git a/oliver/lr_stripes/src/lr_stripes.ino b/oliver/lr_stripes/src/lr_stripes.ino deleted file mode 120000 index a8026eb..0000000 --- a/oliver/lr_stripes/src/lr_stripes.ino +++ /dev/null @@ -1 +0,0 @@ -../lr_stripes.ino \ No newline at end of file diff --git a/rgbtv_light/.gitignore b/rgbtv_light/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/rgbtv_light/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/rgbtv_light/lib/readme.txt b/rgbtv_light/lib/readme.txt new file mode 100644 index 0000000..3f9e008 --- /dev/null +++ b/rgbtv_light/lib/readme.txt @@ -0,0 +1,36 @@ + +This directory is intended for the project specific (private) libraries. +PlatformIO will compile them to static libraries and link to executable file. + +The source code of each library should be placed in separate directory, like +"lib/private_lib/[here are source files]". + +For example, see how can be organized `Foo` and `Bar` libraries: + +|--lib +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| |--Foo +| | |- Foo.c +| | |- Foo.h +| |- readme.txt --> THIS FILE +|- platformio.ini +|--src + |- main.c + +Then in `src/main.c` you should use: + +#include +#include + +// rest H/C/CPP code + +PlatformIO will find your libraries automatically, configure preprocessor's +include paths and build them. + +More information about PlatformIO Library Dependency Finder +- http://docs.platformio.org/en/stable/librarymanager/ldf.html diff --git a/rgbtv_light/platformio.ini b/rgbtv_light/platformio.ini new file mode 100644 index 0000000..6e0f132 --- /dev/null +++ b/rgbtv_light/platformio.ini @@ -0,0 +1,26 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter, extra scripting +; Upload options: custom port, speed and extra flags +; Library options: dependencies, extra library storages +; +; Please visit documentation for the other options and examples +; http://docs.platformio.org/en/stable/projectconf.html + +[platformio] +env_default = pro8MHzatmega328 + +[env:miniatmega328] +platform=atmelavr +board=miniatmega328 +framework=arduino + +[env:pro8MHzatmega328] +platform=atmelavr +board=pro8MHzatmega328 +framework=arduino +; minicore/optirun uses 38400 for 8MHz +upload_speed=38400 + +[platformio] +lib_dir=/home/manuel/coding/Arduino/libraries diff --git a/rgbtv_light/rgbtv_light.ino b/rgbtv_light/rgbtv_light.ino deleted file mode 100644 index 98120a6..0000000 --- a/rgbtv_light/rgbtv_light.ino +++ /dev/null @@ -1,241 +0,0 @@ -/** - * 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 3 - -/** @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 - -#define RELAY_1_PIN 4 // pin number of first relay (second on pin+1 etc) -#define NUMBER_OF_RELAYS 1 // Total number of attached relays -#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 RGB_PIN 7 -#define NUM_LEDS 30 -#define RGB_CHIPSET WS2812B -#define RGB_COLOR_ORDER GRB -#define RGB_CHILD_ID 0 - -#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 -#define TEMP_CHILD_ID 254 - -MyMessage msgRGB(RGB_CHILD_ID, 0); -static uint8_t brightness = 128; - -MyMessage msgRelais(0, V_STATUS); - -unsigned long lastTempUpdate = millis(); -unsigned int numTempUpdates = 0; -float lastTemp = 0; -MyMessage msgTemp(TEMP_CHILD_ID, V_TEMP); - -CRGB leds[NUM_LEDS]; - -void changeRelay(uint8_t relay, uint8_t val, bool send_update=false); - -void before() -{ - // set relay pins to output mode + restore to last known state - for (uint8_t relay = 0; relay < NUMBER_OF_RELAYS; relay++) - { - pinMode(relay + RELAY_1_PIN, OUTPUT); - digitalWrite(relay + RELAY_1_PIN, loadState(relay) ? RELAY_ON : RELAY_OFF); - } - -#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_RFM69_ATC - _radio.enableAutoPower(-70); - debug(PSTR("ATC enabled\n")); -#endif - FastLED.addLeds(leds, NUM_LEDS); - //TODO restore mode(static/ambilight)/color/brightness from flash? - FastLED.setBrightness(brightness); -} - -void presentation() -{ - // Send the sketch version information to the gateway and Controller - sendSketchInfo("Ambilight", "1.0"); - - // Register all sensors to gw (they will be created as child devices) - present(0, S_RGB_LIGHT, "ambilight"); -#if 0 - for (uint8_t relay = 0; relay < NUMBER_OF_RELAYS; relay++) - present(relay + 1, S_BINARY); - present(TEMP_CHILD_ID, S_TEMP); -#endif - - delay(3000); - send(msgRGB.setType(V_STATUS).set(1)); - delay(500); - send(msgRGB.setType(V_DIMMER).set(FastLED.getBrightness())); - delay(500); - send(msgRGB.setType(V_RGB).set("ffffff")); - FastLED.show(); -} - -void loop() -{ - //TODO maybe call _radio.rcCalibration() all 1000x changes? - - //FastLED.show(); - //FastLED.delay(8); - -#if 0 - // check temperature - unsigned long now = millis(); - if (now - lastTempUpdate > TEMP_READ_INTERVAL) - { - float temp = _radio.readTemperature() + TEMP_OFFSET; - lastTempUpdate = now; - if (isnan(temp)) - Serial.println("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; - } -#endif -} - -void receive(const MyMessage &message) -{ - Serial.println(_radio.readRSSI()); - if (message.sensor == RGB_CHILD_ID) - { - if (mGetCommand(message) == C_SET) - { - if (message.type == V_STATUS) - { - bool val = message.getBool(); - // datatype=0, message=0/1 - Serial.println("light on/off"); - //TODO restore brightness. - } - else if (message.type == V_RGB && mGetLength(message) == 6) - { - uint32_t colorcode = strtol(message.getString(), NULL, 16); - fill_solid(leds, NUM_LEDS, CRGB(colorcode)); - FastLED.show(); - } - else if (message.type == V_PERCENTAGE) - { - //TODO fade? - uint8_t val = message.getByte(); - if (val < 0 || val > 100) - return; - Serial.print("dim: "); - Serial.println(val, DEC); - brightness = map(val, 0, 100, 0, 255); - Serial.println(brightness, DEC); - // datatype=0, message=1-100 - FastLED.setBrightness(brightness); - FastLED.show(); - } - } - } - -#if 0 - if (message.type == V_STATUS && message.sensor >= 1) - { - uint8_t relay = message.sensor - 1; - if (relay >= NUMBER_OF_RELAYS) - { - Serial.print("Invalid relay index:"); - Serial.println(relay); - return; - } - - if (mGetCommand(message) == C_REQ) - send(msg.setSensor(relay + 1).set(digitalRead(relay + RELAY_1_PIN))); - else if (mGetCommand(message) == C_SET) - changeRelay(relay, message.getBool() ? RELAY_ON : RELAY_OFF); - } -#endif -} - -void changeRelay(uint8_t relay, uint8_t value, bool send_update) -{ - if (relay >= NUMBER_OF_RELAYS) - return; - Serial.print("Incoming change for relay: "); - Serial.print(relay); - Serial.print(", New status: "); - Serial.println(value); - - // change relay state + store state in eeprom - digitalWrite(relay + RELAY_1_PIN, value); - saveState(relay, value); - - // send msg - if (send_update) - send(msgRelais.setSensor(relay + 1).set(value)); -} - diff --git a/rgbtv_light/src/rgbtv_light.cpp b/rgbtv_light/src/rgbtv_light.cpp new file mode 100644 index 0000000..98120a6 --- /dev/null +++ b/rgbtv_light/src/rgbtv_light.cpp @@ -0,0 +1,241 @@ +/** + * 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 3 + +/** @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 + +#define RELAY_1_PIN 4 // pin number of first relay (second on pin+1 etc) +#define NUMBER_OF_RELAYS 1 // Total number of attached relays +#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 RGB_PIN 7 +#define NUM_LEDS 30 +#define RGB_CHIPSET WS2812B +#define RGB_COLOR_ORDER GRB +#define RGB_CHILD_ID 0 + +#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 +#define TEMP_CHILD_ID 254 + +MyMessage msgRGB(RGB_CHILD_ID, 0); +static uint8_t brightness = 128; + +MyMessage msgRelais(0, V_STATUS); + +unsigned long lastTempUpdate = millis(); +unsigned int numTempUpdates = 0; +float lastTemp = 0; +MyMessage msgTemp(TEMP_CHILD_ID, V_TEMP); + +CRGB leds[NUM_LEDS]; + +void changeRelay(uint8_t relay, uint8_t val, bool send_update=false); + +void before() +{ + // set relay pins to output mode + restore to last known state + for (uint8_t relay = 0; relay < NUMBER_OF_RELAYS; relay++) + { + pinMode(relay + RELAY_1_PIN, OUTPUT); + digitalWrite(relay + RELAY_1_PIN, loadState(relay) ? RELAY_ON : RELAY_OFF); + } + +#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_RFM69_ATC + _radio.enableAutoPower(-70); + debug(PSTR("ATC enabled\n")); +#endif + FastLED.addLeds(leds, NUM_LEDS); + //TODO restore mode(static/ambilight)/color/brightness from flash? + FastLED.setBrightness(brightness); +} + +void presentation() +{ + // Send the sketch version information to the gateway and Controller + sendSketchInfo("Ambilight", "1.0"); + + // Register all sensors to gw (they will be created as child devices) + present(0, S_RGB_LIGHT, "ambilight"); +#if 0 + for (uint8_t relay = 0; relay < NUMBER_OF_RELAYS; relay++) + present(relay + 1, S_BINARY); + present(TEMP_CHILD_ID, S_TEMP); +#endif + + delay(3000); + send(msgRGB.setType(V_STATUS).set(1)); + delay(500); + send(msgRGB.setType(V_DIMMER).set(FastLED.getBrightness())); + delay(500); + send(msgRGB.setType(V_RGB).set("ffffff")); + FastLED.show(); +} + +void loop() +{ + //TODO maybe call _radio.rcCalibration() all 1000x changes? + + //FastLED.show(); + //FastLED.delay(8); + +#if 0 + // check temperature + unsigned long now = millis(); + if (now - lastTempUpdate > TEMP_READ_INTERVAL) + { + float temp = _radio.readTemperature() + TEMP_OFFSET; + lastTempUpdate = now; + if (isnan(temp)) + Serial.println("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; + } +#endif +} + +void receive(const MyMessage &message) +{ + Serial.println(_radio.readRSSI()); + if (message.sensor == RGB_CHILD_ID) + { + if (mGetCommand(message) == C_SET) + { + if (message.type == V_STATUS) + { + bool val = message.getBool(); + // datatype=0, message=0/1 + Serial.println("light on/off"); + //TODO restore brightness. + } + else if (message.type == V_RGB && mGetLength(message) == 6) + { + uint32_t colorcode = strtol(message.getString(), NULL, 16); + fill_solid(leds, NUM_LEDS, CRGB(colorcode)); + FastLED.show(); + } + else if (message.type == V_PERCENTAGE) + { + //TODO fade? + uint8_t val = message.getByte(); + if (val < 0 || val > 100) + return; + Serial.print("dim: "); + Serial.println(val, DEC); + brightness = map(val, 0, 100, 0, 255); + Serial.println(brightness, DEC); + // datatype=0, message=1-100 + FastLED.setBrightness(brightness); + FastLED.show(); + } + } + } + +#if 0 + if (message.type == V_STATUS && message.sensor >= 1) + { + uint8_t relay = message.sensor - 1; + if (relay >= NUMBER_OF_RELAYS) + { + Serial.print("Invalid relay index:"); + Serial.println(relay); + return; + } + + if (mGetCommand(message) == C_REQ) + send(msg.setSensor(relay + 1).set(digitalRead(relay + RELAY_1_PIN))); + else if (mGetCommand(message) == C_SET) + changeRelay(relay, message.getBool() ? RELAY_ON : RELAY_OFF); + } +#endif +} + +void changeRelay(uint8_t relay, uint8_t value, bool send_update) +{ + if (relay >= NUMBER_OF_RELAYS) + return; + Serial.print("Incoming change for relay: "); + Serial.print(relay); + Serial.print(", New status: "); + Serial.println(value); + + // change relay state + store state in eeprom + digitalWrite(relay + RELAY_1_PIN, value); + saveState(relay, value); + + // send msg + if (send_update) + send(msgRelais.setSensor(relay + 1).set(value)); +} + diff --git a/testnode/.gitignore b/testnode/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/testnode/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/testnode/lib/readme.txt b/testnode/lib/readme.txt new file mode 100644 index 0000000..3f9e008 --- /dev/null +++ b/testnode/lib/readme.txt @@ -0,0 +1,36 @@ + +This directory is intended for the project specific (private) libraries. +PlatformIO will compile them to static libraries and link to executable file. + +The source code of each library should be placed in separate directory, like +"lib/private_lib/[here are source files]". + +For example, see how can be organized `Foo` and `Bar` libraries: + +|--lib +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| |--Foo +| | |- Foo.c +| | |- Foo.h +| |- readme.txt --> THIS FILE +|- platformio.ini +|--src + |- main.c + +Then in `src/main.c` you should use: + +#include +#include + +// rest H/C/CPP code + +PlatformIO will find your libraries automatically, configure preprocessor's +include paths and build them. + +More information about PlatformIO Library Dependency Finder +- http://docs.platformio.org/en/stable/librarymanager/ldf.html diff --git a/testnode/platformio.ini b/testnode/platformio.ini new file mode 100644 index 0000000..6e0f132 --- /dev/null +++ b/testnode/platformio.ini @@ -0,0 +1,26 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter, extra scripting +; Upload options: custom port, speed and extra flags +; Library options: dependencies, extra library storages +; +; Please visit documentation for the other options and examples +; http://docs.platformio.org/en/stable/projectconf.html + +[platformio] +env_default = pro8MHzatmega328 + +[env:miniatmega328] +platform=atmelavr +board=miniatmega328 +framework=arduino + +[env:pro8MHzatmega328] +platform=atmelavr +board=pro8MHzatmega328 +framework=arduino +; minicore/optirun uses 38400 for 8MHz +upload_speed=38400 + +[platformio] +lib_dir=/home/manuel/coding/Arduino/libraries diff --git a/testnode/src/testnode.cpp b/testnode/src/testnode.cpp new file mode 100644 index 0000000..999c32a --- /dev/null +++ b/testnode/src/testnode.cpp @@ -0,0 +1,316 @@ +/** + * 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 4 + +/** @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 + +enum sensor_type : uint8_t +{ + SENSOR_RELAY = (1u << 0), + SENSOR_DIMMER = (1u << 1), + SENSOR_BUTTON = (1u << 2), + SENSOR_SCENE = (1u << 3), +}; + +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 = 3 }, + }, + { + .id = 1, + .type = SENSOR_RELAY | SENSOR_DIMMER, + .relay = { .pin = 4 }, + .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 10 // 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); +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_ON); +#endif + } + + if (sensor->type & SENSOR_DIMMER) + { + pinMode(sensor->dimmer.pin, OUTPUT); +#ifdef SAVE_RESTORE + digitalWrite(sensor->relay.pin, loadState(NUM(sensors) + sensor->id)); +#else + analogWrite(sensor->dimmer.pin, map(sensor->dimmer.level, 0, 100, 0, 255)); +#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 +} + +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) + 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(); + if (level > 255) + return; + fadeDimmer(sensor, 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); + +#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); +} + +void fadeDimmer(struct sensor_t *sensor, uint8_t level, bool send_update) +{ + if (!(sensor->type & SENSOR_DIMMER)) + return; + + Serial.print(F("Incoming change for dimmer: ")); + Serial.print(sensor->dimmer.pin); + Serial.print(F(", Old level: ")); + Serial.print(sensor->dimmer.level); + 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, map(sensor->dimmer.level, 0, 100, 0, 255)); + 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)); +} + diff --git a/testnode/testnode.ino b/testnode/testnode.ino deleted file mode 100644 index 999c32a..0000000 --- a/testnode/testnode.ino +++ /dev/null @@ -1,316 +0,0 @@ -/** - * 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 4 - -/** @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 - -enum sensor_type : uint8_t -{ - SENSOR_RELAY = (1u << 0), - SENSOR_DIMMER = (1u << 1), - SENSOR_BUTTON = (1u << 2), - SENSOR_SCENE = (1u << 3), -}; - -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 = 3 }, - }, - { - .id = 1, - .type = SENSOR_RELAY | SENSOR_DIMMER, - .relay = { .pin = 4 }, - .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 10 // 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); -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_ON); -#endif - } - - if (sensor->type & SENSOR_DIMMER) - { - pinMode(sensor->dimmer.pin, OUTPUT); -#ifdef SAVE_RESTORE - digitalWrite(sensor->relay.pin, loadState(NUM(sensors) + sensor->id)); -#else - analogWrite(sensor->dimmer.pin, map(sensor->dimmer.level, 0, 100, 0, 255)); -#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 -} - -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) - 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(); - if (level > 255) - return; - fadeDimmer(sensor, 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); - -#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); -} - -void fadeDimmer(struct sensor_t *sensor, uint8_t level, bool send_update) -{ - if (!(sensor->type & SENSOR_DIMMER)) - return; - - Serial.print(F("Incoming change for dimmer: ")); - Serial.print(sensor->dimmer.pin); - Serial.print(F(", Old level: ")); - Serial.print(sensor->dimmer.level); - 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, map(sensor->dimmer.level, 0, 100, 0, 255)); - 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)); -} - diff --git a/tv_light/.gitignore b/tv_light/.gitignore index e05273b..89cc49c 100644 --- a/tv_light/.gitignore +++ b/tv_light/.gitignore @@ -1,4 +1,5 @@ -.pioenvs -.clang_complete -.gcc-flags.json -.piolibdeps \ No newline at end of file +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/tv_light/src/tv_light.cpp b/tv_light/src/tv_light.cpp new file mode 100644 index 0000000..caf3a6e --- /dev/null +++ b/tv_light/src/tv_light.cpp @@ -0,0 +1,358 @@ +/** + * 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)); +} diff --git a/tv_light/src/tv_light.ino b/tv_light/src/tv_light.ino deleted file mode 100644 index caf3a6e..0000000 --- a/tv_light/src/tv_light.ino +++ /dev/null @@ -1,358 +0,0 @@ -/** - * 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)); -} -- cgit v1.2.3