commit c8e0acf911b67e0629f0e59f6dc82bb9ba2ed269 Author: matzman666 Date: Fri Mar 16 22:25:13 2018 +0100 Initial Commit. diff --git a/.cproject b/.cproject new file mode 100644 index 0000000..5ea270d --- /dev/null +++ b/.cproject @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..5c3c06b --- /dev/null +++ b/.project @@ -0,0 +1,75 @@ + + + MyNotificationLight + + + + + + io.sloeber.core.inoToCpp + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + io.sloeber.arduinonature + + + + core/core + 2 + ECLIPSE_HOME/arduinoPlugin/packages/arduino/hardware/avr/1.6.18/cores/arduino + + + core/variant + 2 + ECLIPSE_HOME/arduinoPlugin/packages/arduino/hardware/avr/1.6.18/variants/eightanaloginputs + + + libraries/Adafruit_GFX_Library + 2 + ECLIPSE_HOME/arduinoPlugin/libraries/Adafruit_GFX_Library/1.1.8 + + + libraries/Adafruit_NeoPixel + 2 + ECLIPSE_HOME/arduinoPlugin/libraries/Adafruit_NeoPixel/1.0.6 + + + libraries/EEPROM + 2 + ECLIPSE_HOME/arduinoPlugin/packages/arduino/hardware/avr/1.6.18/libraries/EEPROM + + + libraries/MyNodeManager + 2 + ECLIPSE_HOME/arduinoPlugin/libraries/MyNodeManager/1.0.0 + + + libraries/MySensors + 2 + ECLIPSE_HOME/arduinoPlugin/libraries/MySensors/2.2.0 + + + libraries/SPI + 2 + ECLIPSE_HOME/arduinoPlugin/packages/arduino/hardware/avr/1.6.18/libraries/SPI + + + diff --git a/MyNotificationLight.ino b/MyNotificationLight.ino new file mode 100644 index 0000000..c0bcd9d --- /dev/null +++ b/MyNotificationLight.ino @@ -0,0 +1,104 @@ + +#include +#include +#include +#include "NotificationLEDs.h" + + +#define SENSOR_CHECK_INTERVAL 2000 +#define MSG_SEND_CYCLES 15 +#define MSG_SEND_INTERVAL 30000 + +#define PIN_BUZZER 3 +#define PIN_RGBW 5 +#define PIN_MOTION 6 +#define PIN_DOOR0 A0 +#define PIN_DOOR1 A1 +#define PIN_DOOR2 A2 +#define PIN_DOOR3 A3 +#define PIN_MOTION2 A5 + +#define SENSOR_ID_RGBW 0 +#define SENSOR_ID_DIMMER 1 +#define SENSOR_ID_POWERSWITCH 2 +#define SENSOR_ID_COMMAND 3 +#define SENSOR_ID_MOTION 10 +#define SENSOR_ID_DOOR0 11 +#define SENSOR_ID_DOOR1 12 +#define SENSOR_ID_DOOR2 13 +#define SENSOR_ID_DOOR3 14 +#define SENSOR_ID_MOTION2 15 +#define SENSOR_ID_VCC 210 + +#define DIN_DEBOUNCE 6 + +#define UNIXTIME_FETCH_INTERVAL_SEC 300 + +#define NEOPIXEL_LEDCOUNT 16 + + + + +// create a NodeManager instance +typedef NodeManager< + Tuple< + NotificationLEDs, + SensorMotion, + SensorMotion, + SensorDoor, + SensorDoor, + SensorDoor, + SensorDoor, + SensorVcc + > +> MyNodeManagerBase; + +class MyNodeManager : public MyNodeManagerBase { +protected: +} nodeManager; + + +void before() { + //nodeManager.setSleep(ALWAYS_ON, 1, SECONDS); + nodeManager.before(); +} + + +void presentation() { + nodeManager.presentation(); +} + + +void receive(const MyMessage &message) { + if (!message.isAck()) { + nodeManager.receive(message); + } +} + + +unsigned long unixTime = 0; +unsigned long unixTimeMillis = 0; + +void receiveTime(unsigned long ts) { + unixTime = ts; + unixTimeMillis = millis(); + nodeManager.receiveTime(ts); +} + + +void setup() { + nodeManager.setup(); +} + + +void loop() { + auto currentMillis = millis(); + auto diffMillis = currentMillis - unixTimeMillis; + auto diffSeconds = diffMillis/1000; + long currentUnixTime = unixTime + diffSeconds; + if (unixTime == 0 || diffSeconds >= UNIXTIME_FETCH_INTERVAL_SEC) { + requestTime(); + } + nodeManager.loop(currentUnixTime, currentMillis); +} + diff --git a/NotificationLEDs.h b/NotificationLEDs.h new file mode 100644 index 0000000..eb60dda --- /dev/null +++ b/NotificationLEDs.h @@ -0,0 +1,622 @@ +#ifndef NOTIFICATIONLEDS_H_ +#define NOTIFICATIONLEDS_H_ + + +#include +#include +#include +#include + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval = 60000 +> class NotificationLEDs : public Sensor { +public: + + void before() noexcept; + void setup() noexcept; + void presentation() noexcept; + void loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept; + bool process(const MyMessage& msg) noexcept; + + void enableDefaultScene(int id = -1) noexcept; + void setStaticColor(uint32_t color) noexcept; + void enableStaticColor() noexcept; + void showNotification(int id = -1) noexcept; + void powerOff() noexcept; + void powerOn() noexcept; + void returnToLastMode() noexcept; + + void defaultScene_config(uint8_t id) noexcept; + void defaultScene_init() noexcept; + void defaultScene_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept; + + void overrideScene_config(uint8_t id) noexcept; + void overrideScene_init() noexcept; + void overrideScene_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept; + + void analogClock_config() noexcept; + void analogClock_init() noexcept; + void analogClock_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept; + + void colorWheel1_config() noexcept; + void colorWheel1_init() noexcept; + void colorWheel1_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept; + + void colorWheel2_config() noexcept; + void colorWheel2_init() noexcept; + void colorWheel2_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept; + + void pulse1_config() noexcept; + void pulse1_init() noexcept; + void pulse1_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept; + +protected: + unsigned long lastSendTime = 0; + Adafruit_NeoPixel pixels = Adafruit_NeoPixel(_tpixelCount, _tpin, NEO_GRBW + NEO_KHZ800); + + uint8_t brightness = 25; + + uint8_t mode = 1; // 0: power down, 1: normal, 2: notification + uint8_t lastMode = 1; + uint8_t sceneMode = 0; // 0: default scene, 1: static color + + uint8_t defaultScene = 0; // 0: AnalogClock + uint8_t overrideScene = 0; + + const char* _configDelims = ":"; + + union { + uint8_t wrgb[4]; + uint32_t color; + } staticColor; + bool staticColorOn = true; + + struct { + struct { + int8_t hourOffset = 1; + bool showInnerClock = true; + bool temporaryDisableInnerClock = false; + unsigned long lastUnixTime = 0; + } analogClock; + struct { + uint16_t sleepTime = 80; + int8_t pixelStep = 1; + int8_t colorStep = 20; + int16_t currentColorIndex = 0; + int16_t currentPixelIndex = 0; + unsigned long lastMillis = 0; + } colorWheel1; + struct { + uint16_t sleepTime = 80; + int8_t colorStep = 20; + int16_t currentColorIndex = 0; + unsigned long lastMillis = 0; + } colorWheel2; + struct { + union { + uint8_t wrgb[4]; + uint32_t color = 0x00FF0000; + } color; + uint8_t maxPulses = 5; + uint8_t currentPulses = 0; + int8_t pulseDir = 10; + uint8_t pulseValue = 0; + unsigned long lastMillis = 0; + } pulse1; + } sceneData; +}; + + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::before() noexcept {} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::setup() noexcept { + pixels.begin(); + defaultScene_init(); +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::presentation() noexcept { + String desc(F("RGBW")); + present(_trgbwId, S_RGBW_LIGHT, desc.c_str()); + desc = F("Dimmer"); + present(_tdimmerId, S_DIMMER, desc.c_str()); + desc = F("PowerSwitch"); + present(_tpowerSwitchId, S_BINARY, desc.c_str()); + desc = F("Command"); + present(_tcommandId, S_INFO, desc.c_str()); +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept { + + // default scene + if (mode == 1 && sceneMode == 0) { + defaultScene_loop(currentUnixTime, currentMillis); + } else if (mode == 2) { + overrideScene_loop(currentUnixTime, currentMillis); + } + + if (lastSendTime == 0 || currentMillis - lastSendTime >= _msgSendInterval) { + + // RGBW Sensor + char rgbw[9] = "00000000"; + // WRGB => RGBW + String tmp((staticColor.color & 0xFF000000) >> 24 | ((staticColor.color & 0x00FFFFFF) << 8)); + memcpy(rgbw+(8-tmp.length()), tmp.c_str(), tmp.length()); + _sendValue(rgbw, _trgbwId, V_RGBW); + + //Dimmer + if (sceneMode == 0) { + _sendValue(brightness, _tdimmerId, V_STATUS); + } else { + uint16_t cb = max(staticColor.wrgb[0], max(staticColor.wrgb[1], max(staticColor.wrgb[2], staticColor.wrgb[3]))) * 100 / 255; + _sendValue(cb, _tdimmerId, V_STATUS); + } + + //Power Switch + _sendValue(mode == 0 ? 0 : 1, _tpowerSwitchId, V_STATUS); + + lastSendTime = currentMillis; + } +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> bool NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::process(const MyMessage& msg) noexcept { + bool retval = false; + + // RGBW Sensor + if (msg.sensor == _trgbwId) { + // Set Message + if (msg.getCommand() == C_SET) { + if (msg.type == V_STATUS) { + auto onOff = msg.getByte(); + if (onOff) { + enableStaticColor(); + } else { + enableDefaultScene(); + } + retval = true; + } else if (msg.type == V_PERCENTAGE) { + uint16_t cb = (uint16_t)max(staticColor.wrgb[0], max(staticColor.wrgb[1], max(staticColor.wrgb[2], staticColor.wrgb[3]))) * 100 / 255; + uint16_t nb = msg.getByte(); + for (auto i = 0; i < 4; i++) { + uint16_t newcolor = ((uint16_t)staticColor.wrgb[i] * nb) / cb; + if (newcolor > 255) { + newcolor = 255; + } + staticColor.wrgb[i] = newcolor; + } + setStaticColor(staticColor.color); + retval = true; + } else if (msg.type == V_RGBW) { + uint32_t color = strtoul(msg.data, NULL, 16); + // MySensors uses "RGBW" format, but the NeoPixel library uses "WRGB" format + setStaticColor((color & 0xFFFFFF00) >> 8 | ((color & 0xFF) << 24)); + retval = true; + } + // Request Message + } else if (msg.getCommand() == C_REQ) { + char rgbw[9] = "00000000"; + // WRGB => RGBW + String tmp((staticColor.color & 0xFF000000) >> 24 | ((staticColor.color & 0x00FFFFFF) << 8)); + memcpy(rgbw+(8-tmp.length()), tmp.c_str(), tmp.length()); + _sendValue(rgbw, _trgbwId, V_RGBW); + retval = true; + } + } + + // Dimmer Sensor + else if (msg.sensor == _tdimmerId) { + // Set Message + if (msg.getCommand() == C_SET) { + if (msg.type == V_PERCENTAGE) { + auto b = msg.getByte(); + if (sceneMode == 0) { + brightness = b; + sceneData.analogClock.lastUnixTime = 0; + } else { + uint16_t cb = max(staticColor.wrgb[0], max(staticColor.wrgb[1], max(staticColor.wrgb[2], staticColor.wrgb[3]))) * 100 / 255; + uint16_t nb = msg.getByte(); + for (auto i = 0; i < 4; i++) { + uint16_t newcolor = ((uint16_t)staticColor.wrgb[i] * nb) / cb; + if (newcolor > 255) { + newcolor = 255; + } + staticColor.wrgb[i] = newcolor; + } + setStaticColor(staticColor.color); + } + retval = true; + } + // Request Message + } else if (msg.getCommand() == C_REQ) { + if (sceneMode == 0) { + _sendValue(brightness, _tdimmerId, V_STATUS); + } else { + uint16_t cb = max(staticColor.wrgb[0], max(staticColor.wrgb[1], max(staticColor.wrgb[2], staticColor.wrgb[3]))) * 100 / 255; + _sendValue(cb, _tdimmerId, V_STATUS); + } + retval = true; + } + } + + // Power Switch + else if (msg.sensor == _tpowerSwitchId) { + // Set Message + if (msg.getCommand() == C_SET) { + if (msg.type == V_STATUS) { + auto v = msg.getByte(); + if (v == 0) { + powerOff(); + } else { + powerOn(); + } + retval = true; + } + // Request Message + } else if (msg.getCommand() == C_REQ) { + _sendValue(mode == 0 ? 0 : 1, _tpowerSwitchId, V_STATUS); + retval = true; + } + } + + // Command + else if (msg.sensor == _tcommandId) { + // Set Message + if (msg.getCommand() == C_SET) { + if (msg.type == V_TEXT) { + auto v = msg.getString(); + + Serial.print("Command: "); + Serial.println(v); + + auto cmd = strtok((char*)v, _configDelims); + if (cmd[0] == 'D') { // Set default Scene + auto id = atoi(cmd+1); + defaultScene_config(id); + if (mode == 1 && sceneMode == 0) { + defaultScene_init(); + } + } else if (cmd[0] == 'N') { // Notification + if (cmd[1] == 'C') { + returnToLastMode(); + } else if (cmd[1] == '\0') { + showNotification(); + } else { + auto id = atoi(cmd+1); + showNotification(id); + } + } + retval = true; + } + // Request Message + } else if (msg.getCommand() == C_REQ) { + retval = true; + } + } + + return retval; +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::enableDefaultScene(int id) { + sceneMode = 0; + mode = 1; + if (id >= 0) { + defaultScene = id; + } + defaultScene_init(); +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::setStaticColor(uint32_t color) { + sceneMode = 1; + mode = 1; + if (color == 0) { + staticColorOn = false; + for (unsigned i = 0; i < _tpixelCount; i++) { + pixels.setPixelColor(i, 0); + } + } else { + staticColorOn = true; + staticColor.color = color; + for (unsigned i = 0; i < _tpixelCount; i++) { + pixels.setPixelColor(i, staticColor.color); + } + } + pixels.show(); +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::enableStaticColor() noexcept { + sceneMode = 1; + mode = 1; + if (staticColorOn) { + for (unsigned i = 0; i < _tpixelCount; i++) { + pixels.setPixelColor(i, staticColor.color); + } + } else { + for (unsigned i = 0; i < _tpixelCount; i++) { + pixels.setPixelColor(i, 0); + } + } + pixels.show(); +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::showNotification(int id) noexcept { + if (mode < 2) { + lastMode = mode; + } + mode = 2; + if (id >= 0) { + overrideScene_config(id); + } + overrideScene_init(); +} + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::powerOff() noexcept { + mode = 0; + for (unsigned i = 0; i < _tpixelCount; i++) { + pixels.setPixelColor(i, 0); + } + pixels.show(); +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::powerOn() noexcept { + mode = 1; + if (sceneMode == 0) { + enableDefaultScene(); + } else { + enableStaticColor(); + } + pixels.show(); +} + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::returnToLastMode() noexcept { + if (lastMode == 0) { + powerOff(); + } else if (lastMode == 1) { + powerOn(); + } +} + + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::defaultScene_config(uint8_t id){ + if (id == 0) { + defaultScene = id; + analogClock_config(); + } else if (id == 1) { + defaultScene = id; + colorWheel1_config(); + } else if (id == 2) { + defaultScene = id; + colorWheel2_config(); + } +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::defaultScene_init() noexcept { + if (defaultScene == 0) { + analogClock_init(); + } else if (defaultScene == 1) { + colorWheel1_init(); + } else if (defaultScene == 2) { + colorWheel2_init(); + } +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::defaultScene_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept { + if (defaultScene == 0) { + analogClock_loop(currentUnixTime, currentMillis); + } else if (defaultScene == 1) { + colorWheel1_loop(currentUnixTime, currentMillis); + } else if (defaultScene == 2) { + colorWheel2_loop(currentUnixTime, currentMillis); + } +} + + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::overrideScene_config(uint8_t id){ + if (id == 0) { + overrideScene = id; + pulse1_config(); + } +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::overrideScene_init() noexcept { + if (overrideScene == 0) { + pulse1_init(); + } +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::overrideScene_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept { + if (overrideScene == 0) { + pulse1_loop(currentUnixTime, currentMillis); + } +} + +#include "SceneAnalogClock.h" +#include "SceneColorWheel.h" +#include "ScenePulse.h" + + + +#endif /* NOTIFICATIONLEDS_H_ */ diff --git a/SceneAnalogClock.h b/SceneAnalogClock.h new file mode 100644 index 0000000..4bb601a --- /dev/null +++ b/SceneAnalogClock.h @@ -0,0 +1,125 @@ +#ifndef SCENEANALOGCLOCK_H_ +#define SCENEANALOGCLOCK_H_ + +#include +#include +#include +#include +#include "NotificationLEDs.h" + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::analogClock_config() noexcept { + auto token = strtok(NULL, _configDelims); + if (token && token[0] != ' ') { + auto offset = atoi(token); + sceneData.analogClock.hourOffset = offset; + } + token = strtok(NULL, _configDelims); + if (token && token[0] != ' ') { + bool innerClock = atoi(token); + sceneData.analogClock.showInnerClock = innerClock; + } + sceneData.analogClock.temporaryDisableInnerClock = false; + sceneData.analogClock.lastUnixTime = 0; +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::analogClock_init() noexcept { + for (uint8_t i = 0; i < 12; i++) { + pixels.setPixelColor(i, 0); + } + if (sceneData.analogClock.showInnerClock && !sceneData.analogClock.temporaryDisableInnerClock) { + for (uint8_t i = 12; i < 16; i++) { + pixels.setPixelColor(i, 0); + } + } + pixels.show(); + sceneData.analogClock.lastUnixTime = 0; +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::analogClock_loop(unsigned long currentUnixTime, unsigned long) noexcept { + if (sceneData.analogClock.lastUnixTime != currentUnixTime) { + auto seconds = currentUnixTime % 60; + auto _tmp = currentUnixTime / 60; + auto minutes = _tmp % 60; + auto hours = (_tmp / 60 + sceneData.analogClock.hourOffset) % 12; + auto p_h = (11 - hours) % 12; + auto p_m = (11 - minutes/5) % 12; + auto p_m2 = minutes % 5; + auto p_s = (11 - seconds/5) % 12; + auto p_s2 = seconds % 5; + /*Serial.print("Time: "); + Serial.print(hours); + Serial.print(":"); + Serial.print(minutes); + Serial.print(":"); + Serial.println(seconds); + Serial.print("Pixels: "); + Serial.print(p_h); + Serial.print(":"); + Serial.print(p_m); + Serial.print(":"); + Serial.println(p_s);*/ + for (uint8_t i = 0; i < 12; i++) { + uint32_t c = 0; + if (i == p_h) { // hour => red + c |= ((uint32_t)255*(uint32_t)brightness/(uint32_t)100) << 16; + } + if (i == p_m) { // min => green + c |= ((uint32_t)255*(uint32_t)brightness/(uint32_t)100) << 8; + } + if (i == p_s) { // sec => blue + c |= (uint32_t)255*(uint32_t)brightness/(uint32_t)100; + } + if (c == 0) { + c = ((uint32_t)20*(uint32_t)brightness/(uint32_t)100) << 24; + } + pixels.setPixelColor(i, c); + } + if (sceneData.analogClock.showInnerClock && !sceneData.analogClock.temporaryDisableInnerClock) { + for (uint8_t i = 0; i < 4; i++) { + uint32_t c = 0; + if (i < p_s2) { + c |= (uint32_t)255*(uint32_t)brightness/(uint32_t)100; + } + if (i < p_m2) { + c |= ((uint32_t)255*(uint32_t)brightness/(uint32_t)100) << 8; + } + pixels.setPixelColor(15 - i, c); + } + } + pixels.show(); + sceneData.analogClock.lastUnixTime = currentUnixTime; + } +} + + +#endif /* SCENEANALOGCLOCK_H_ */ diff --git a/SceneColorWheel.h b/SceneColorWheel.h new file mode 100644 index 0000000..f7af58c --- /dev/null +++ b/SceneColorWheel.h @@ -0,0 +1,176 @@ +#ifndef SCENECOLORWHEEL_H_ +#define SCENECOLORWHEEL_H_ + +#include +#include +#include +#include +#include "NotificationLEDs.h" + + +void _colorWheel_mapIndex(uint16_t c, uint16_t& r, uint16_t& g, uint16_t& b) { + if (c < 255) { + r = 255 - c; + g = c; + b = 0; + } else if (c >= 255 && c < 510) { + r = 0; + g = 510 - c; + b = c - 255; + } else if (c >= 510 && c < 765) { + r = c - 510; + g = 0; + b = 765 - c; + } else { + r = g = b = 255; + } +} + +// mathematically correct modulo +#define MOD(a,b) ((((a)%(b))+(b))%(b)) + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::colorWheel1_config() noexcept { + auto token = strtok(NULL, _configDelims); + if (token && token[0] != ' ') { + auto pixelStep = atoi(token); + sceneData.colorWheel1.pixelStep = pixelStep; + } + token = strtok(NULL, _configDelims); + if (token && token[0] != ' ') { + auto colorStep = atoi(token); + sceneData.colorWheel1.colorStep = colorStep; + } + token = strtok(NULL, _configDelims); + if (token && token[0] != ' ') { + auto sleepTime = atoi(token); + sceneData.colorWheel1.sleepTime = sleepTime; + } +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::colorWheel1_init() noexcept { + for (uint8_t i = 0; i < _tpixelCount; i++) { + pixels.setPixelColor(i, 0); + } + pixels.show(); + sceneData.colorWheel1.currentColorIndex = 0; + sceneData.colorWheel1.currentPixelIndex = 0; + sceneData.colorWheel1.lastMillis = 0; +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::colorWheel1_loop(unsigned long, unsigned long currentMillis) noexcept { + if (currentMillis - sceneData.colorWheel1.lastMillis >= sceneData.colorWheel1.sleepTime) { + uint16_t r, g, b; + _colorWheel_mapIndex(sceneData.colorWheel1.currentColorIndex, r, g, b); + r = r*(uint16_t)brightness/100; + g = g*(uint16_t)brightness/100; + b = b*(uint16_t)brightness/100; + pixels.setPixelColor(sceneData.colorWheel1.currentPixelIndex, r, g, b, 0); + pixels.setPixelColor(12, r, g, b, 0); + pixels.setPixelColor(13, r, g, b, 0); + pixels.setPixelColor(14, r, g, b, 0); + pixels.setPixelColor(15, r, g, b, 0); + pixels.show(); + sceneData.colorWheel1.currentColorIndex = MOD(sceneData.colorWheel1.currentColorIndex + sceneData.colorWheel1.colorStep, 765); + sceneData.colorWheel1.currentPixelIndex = MOD(sceneData.colorWheel1.currentPixelIndex + sceneData.colorWheel1.pixelStep, 12); + sceneData.colorWheel1.lastMillis = currentMillis; + } +} + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::colorWheel2_config() noexcept { + auto token = strtok(NULL, _configDelims); + if (token && token[0] != ' ') { + auto colorStep = atoi(token); + sceneData.colorWheel2.colorStep = colorStep; + } + token = strtok(NULL, _configDelims); + if (token && token[0] != ' ') { + auto sleepTime = atoi(token); + sceneData.colorWheel2.sleepTime = sleepTime; + } +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::colorWheel2_init() noexcept { + for (uint8_t i = 0; i < _tpixelCount; i++) { + pixels.setPixelColor(i, 0); + } + pixels.show(); + sceneData.colorWheel2.currentColorIndex = 0; + sceneData.colorWheel2.lastMillis = 0; +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::colorWheel2_loop(unsigned long, unsigned long currentMillis) noexcept { + if (currentMillis - sceneData.colorWheel2.lastMillis >= sceneData.colorWheel2.sleepTime) { + uint16_t r, g, b; + _colorWheel_mapIndex(sceneData.colorWheel2.currentColorIndex, r, g, b); + r = r*(uint16_t)brightness/100; + g = g*(uint16_t)brightness/100; + b = b*(uint16_t)brightness/100; + for (uint16_t i = 0; i < _tpixelCount; i++) { + pixels.setPixelColor(i, r, g, b, 0); + } + pixels.show(); + sceneData.colorWheel2.currentColorIndex = MOD(sceneData.colorWheel2.currentColorIndex + sceneData.colorWheel2.colorStep, 765); + sceneData.colorWheel2.lastMillis = currentMillis; + } +} + + +#endif /* SCENECOLORWHEEL_H_ */ diff --git a/ScenePulse.h b/ScenePulse.h new file mode 100644 index 0000000..519145d --- /dev/null +++ b/ScenePulse.h @@ -0,0 +1,103 @@ +#ifndef SCENEPULSE_H_ +#define SCENEPULSE_H_ + +#include +#include +#include +#include +#include "NotificationLEDs.h" + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::pulse1_config() noexcept { + auto token = strtok(NULL, _configDelims); + if (token && token[0] != ' ') { + auto color = strtoul(token, NULL, 16); + sceneData.pulse1.color.color = color; + } + token = strtok(NULL, _configDelims); + if (token && token[0] != ' ') { + auto maxPulses = atoi(token); + sceneData.pulse1.maxPulses = maxPulses; + } + token = strtok(NULL, _configDelims); + if (token && token[0] != ' ') { + auto pulseDir = atoi(token); + sceneData.pulse1.pulseDir = pulseDir; + } +} + + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::pulse1_init() noexcept { + for (uint8_t i = 0; i < _tpixelCount; i++) { + pixels.setPixelColor(i, 0); + } + pixels.show(); + sceneData.pulse1.pulseValue = 0; + sceneData.pulse1.lastMillis = 0; + sceneData.pulse1.currentPulses = 0; +} + + +template +T tmap(T x, T in_min, T in_max, T out_min, T out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + +template< + uint8_t _tpin, + uint8_t _trgbwId, + uint8_t _tdimmerId, + uint8_t _tpowerSwitchId, + uint8_t _tcommandId, + uint16_t _tpixelCount, + unsigned long _msgSendInterval +> void NotificationLEDs<_tpin, _trgbwId, _tdimmerId, _tpowerSwitchId, _tcommandId, _tpixelCount, _msgSendInterval> +::pulse1_loop(unsigned long currentUnixTime, unsigned long currentMillis) noexcept { + if (currentMillis - sceneData.pulse1.lastMillis >= 50) { + auto color = sceneData.pulse1.color; + int16_t pulseValue = sceneData.pulse1.pulseValue; + uint16_t br = 127.5 * (-cos(tmap(pulseValue, 0, 255, 0, 2.0*M_PI))+1.0); + color.wrgb[0] = (uint16_t)color.wrgb[0]*br/255; + color.wrgb[1] = (uint16_t)color.wrgb[1]*br/255; + color.wrgb[2] = (uint16_t)color.wrgb[2]*br/255; + color.wrgb[3] = (uint16_t)color.wrgb[3]*br/255; + for (uint16_t i = 0; i < _tpixelCount; i++) { + pixels.setPixelColor(i, color.color); + } + pixels.show(); + pulseValue += sceneData.pulse1.pulseDir; + if (pulseValue < 0) { + sceneData.pulse1.pulseDir = -sceneData.pulse1.pulseDir; + pulseValue = sceneData.pulse1.pulseDir; + sceneData.pulse1.currentPulses++; + if (sceneData.pulse1.maxPulses > 0 && sceneData.pulse1.currentPulses > sceneData.pulse1.maxPulses) { + returnToLastMode(); + } + } else if (pulseValue > 255) { + sceneData.pulse1.pulseDir = -sceneData.pulse1.pulseDir; + pulseValue = 255 + sceneData.pulse1.pulseDir; + } + sceneData.pulse1.pulseValue = pulseValue; + sceneData.pulse1.lastMillis = currentMillis; + } +} + +#endif /* SCENEPULSE_H_ */ diff --git a/nodemanager_config.h b/nodemanager_config.h new file mode 100644 index 0000000..31f67c1 --- /dev/null +++ b/nodemanager_config.h @@ -0,0 +1,70 @@ +#ifndef config_h +#define config_h + +/********************************** + * Sketch configuration + */ + +#define SKETCH_NAME "Notification01" +#define SKETCH_VERSION "1.0" + +/********************************** + * MySensors node configuration + */ + +#define MY_NODE_ID 1 + +#define MY_MSG_ACK 1 +#define MY_MSG_RETRIES 3 +#define MY_MSG_RETRY_DELAY 1 + + +// Enable debug prints to serial monitor +#define MY_DEBUG +//#define MY_DEBUG_VERBOSE_RF24 +//#define MY_SPECIAL_DEBUG +#define MY_SPLASH_SCREEN_DISABLED + + +// Enable and select radio type attached +#define MY_RADIO_NRF24 + +#define MY_RF24_PA_LEVEL RF24_PA_MAX +#define MY_RF24_DATARATE RF24_250KBPS +#define MY_RF24_CHANNEL 82 +#define MY_RF24_BASE_RADIO_ID 0x00,0x30,0x08,0x19,0x82 +#define MY_RF24_ENABLE_XTEA +#define MY_RF24_XTEA_KEY { 0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87, 0x78, 0x69, 0x5A, 0x4B, 0x3C, 0x2D, 0x1E, 0x0F } +#define MY_MESSAGE_ENABLE_NONCE +#define MY_MESSAGE_ENABLE_HASH +#define MY_SMART_SLEEP_WAIT_DURATION_MS 60 + +#define MY_RF24_CE_PIN 9 +#define MY_RF24_CS_PIN 10 + + +/*********************************** + * NodeManager configuration + */ + +#define DEBUG 0 + +// if enabled, enable the capability to power on sensors with the arduino's pins to save battery while sleeping +#define POWER_MANAGER 0 +// if enabled, will load the battery manager library to allow the battery level to be reported automatically or on demand +#define BATTERY_MANAGER 0 +// if enabled, allow modifying the configuration remotely by interacting with the configuration child id +#define REMOTE_CONFIGURATION 1 +// if enabled, persist the remote configuration settings on EEPROM +#define PERSIST 1 +// if enabled, a battery sensor will be created at BATTERY_CHILD_ID and will report vcc voltage together with the battery level percentage +#define BATTERY_SENSOR 0 +// if enabled, send a SLEEPING and AWAKE service messages just before entering and just after leaving a sleep cycle and STARTED when starting/rebooting +#define SERVICE_MESSAGES 0 + +#define DISABLE_SLEEP_MODE + + + +#endif +