diff --git a/_config.yml b/_config.yml new file mode 100644 index 00000000..c4192631 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/libraries/SuplaDevice/SuplaDevice.cpp b/libraries/SuplaDevice/SuplaDevice.cpp index 6795f13f..abf9aabb 100644 --- a/libraries/SuplaDevice/SuplaDevice.cpp +++ b/libraries/SuplaDevice/SuplaDevice.cpp @@ -16,10 +16,9 @@ #define SUPLADEVICE_CPP -#include "SuplaDevice.h" - #include +#include "SuplaDevice.h" #include "SuplaImpulseCounter.h" #include "io.h" #include "supla-common/IEEE754tools.h" @@ -82,7 +81,7 @@ void SuplaDeviceClass::status(int status, const char *msg) { } } -SuplaDeviceClass::SuplaDeviceClass() { +SuplaDeviceClass::SuplaDeviceClass() : port(-1) { srpc = NULL; registered = 0; last_iterate_time = 0; @@ -493,48 +492,6 @@ void SuplaDeviceClass::setRollerShutterButtons(int channel_number, } } -bool SuplaDeviceClass::addSensorNO(int sensorPin, bool pullUp) { - int c = addChannel(sensorPin, 0, false, false); - if (c == -1) return false; - - Supla::Channel::reg_dev.channels[c].Type = SUPLA_CHANNELTYPE_SENSORNO; - if (pullUp) { - pinMode(sensorPin, INPUT_PULLUP); - } else { - pinMode(sensorPin, INPUT); - } - - Supla::Channel::reg_dev.channels[c].value[0] = - Supla::Io::digitalRead(Supla::Channel::reg_dev.channels[c].Number, - sensorPin) == HIGH - ? 1 - : 0; - return true; -} - -void SuplaDeviceClass::setDoubleValue(char value[SUPLA_CHANNELVALUE_SIZE], - double v) { - if (sizeof(double) == 8) { - memcpy(value, &v, 8); - } else if (sizeof(double) == 4) { - float2DoublePacked(v, (uint8_t *)value); - } -} - -void SuplaDeviceClass::channelSetDoubleValue(int channelNum, double value) { - setDoubleValue(Supla::Channel::reg_dev.channels[channelNum].value, value); -} - -void SuplaDeviceClass::channelSetTempAndHumidityValue(int channelNum, - double temp, - double humidity) { - long t = temp * 1000.00; - long h = humidity * 1000.00; - - memcpy(Supla::Channel::reg_dev.channels[channelNum].value, &t, 4); - memcpy(&Supla::Channel::reg_dev.channels[channelNum].value[4], &h, 4); -} - void SuplaDeviceClass::setRGBWvalue(int channelNum, char value[SUPLA_CHANNELVALUE_SIZE]) { if (Params.cb.get_rgbw_value) { @@ -585,10 +542,6 @@ bool SuplaDeviceClass::addDimmer(void) { setRGBWvalue(c, Supla::Channel::reg_dev.channels[c].value); } -bool SuplaDeviceClass::addSensorNO(int sensorPin) { - return addSensorNO(sensorPin, false); -} - bool SuplaDeviceClass::addImpulseCounter(int impulsePin, int statusLedPin, bool detectLowToHigh, @@ -691,25 +644,6 @@ void SuplaDeviceClass::iterate_relay(SuplaChannelPin *pin, } } -void SuplaDeviceClass::iterate_sensor(SuplaChannelPin *pin, - TDS_SuplaDeviceChannel_C *channel, - unsigned long time_diff, - int channel_number) { - if (channel->Type == SUPLA_CHANNELTYPE_SENSORNO) { - uint8_t val = Supla::Io::digitalRead(channel->Number, pin->pin1); - - if (val != pin->last_val) { - pin->last_val = val; - Supla::Channel::reg_dev.channels[channel->Number].value[0] = val; - - if (pin->time_left <= 0) { - pin->time_left = 100; - channelValueChanged(channel->Number, val == HIGH ? 1 : 0); - } - } - } -}; - void SuplaDeviceClass::rs_save_position(SuplaDeviceRollerShutter *rs) { if (impl_rs_save_position) { impl_rs_save_position(rs->channel_number, rs->position); @@ -1038,7 +972,7 @@ void SuplaDeviceClass::iterate_rollershutter( if (rs->last_position != rs->position) { rs->last_position = rs->position; - channelValueChanged(rs->channel_number, (rs->position - 100) / 100, 0, 1); + channelValueChanged(rs->channel_number, (rs->position - 100) / 100); } if (rs->up_time > 600000 || rs->down_time > 600000) { // 10 min. - timeout @@ -1124,7 +1058,6 @@ void SuplaDeviceClass::iterate(void) { } if (!Supla::Network::Connected()) { - int port = 2015; /* deprecated in ESP8266 */ status(STATUS_DISCONNECTED, "Not connected"); registered = 0; @@ -1178,10 +1111,6 @@ void SuplaDeviceClass::iterate(void) { &Supla::Channel::reg_dev.channels[a], time_diff, a); - iterate_sensor(&channel_pin[a], - &Supla::Channel::reg_dev.channels[a], - time_diff, - a); iterate_impulse_counter(&channel_pin[a], &Supla::Channel::reg_dev.channels[a], time_diff, @@ -1288,18 +1217,12 @@ void SuplaDeviceClass::onRegisterResult( wait_for_iterate = millis() + 5000; } -void SuplaDeviceClass::channelValueChanged(int channel_number, - char v, - double d, - char var) { +void SuplaDeviceClass::channelValueChanged(int channel_number, char v) { if (srpc != NULL && registered == 1) { char value[SUPLA_CHANNELVALUE_SIZE]; memset(value, 0, SUPLA_CHANNELVALUE_SIZE); - if (var == 1) - value[0] = v; - else if (var == 2) - setDoubleValue(value, d); + value[0] = v; memcpy(Supla::Channel::reg_dev.channels[channel_number].value, value, 8); supla_log( @@ -1309,14 +1232,6 @@ void SuplaDeviceClass::channelValueChanged(int channel_number, } } -void SuplaDeviceClass::channelDoubleValueChanged(int channel_number, double v) { - channelValueChanged(channel_number, 0, v, 2); -} - -void SuplaDeviceClass::channelValueChanged(int channel_number, char v) { - channelValueChanged(channel_number, v, 0, 1); -} - void SuplaDeviceClass::channelSetValue(int channel, char value, _supla_int_t DurationMS) { @@ -1551,6 +1466,10 @@ bool SuplaDeviceClass::rollerShutterMotorIsOn(int channel_number) { channel_pin[channel_number].pin2)); } +void SuplaDeviceClass::setServerPort(int value) { + port = value; +} + SuplaDeviceClass SuplaDevice; SuplaDeviceCallbacks supla_arduino_get_callbacks(void) { diff --git a/libraries/SuplaDevice/SuplaDevice.h b/libraries/SuplaDevice/SuplaDevice.h index d2dc9f7c..96d862c5 100644 --- a/libraries/SuplaDevice/SuplaDevice.h +++ b/libraries/SuplaDevice/SuplaDevice.h @@ -44,11 +44,6 @@ #define STATUS_LOCATION_IS_DISABLED 19 #define STATUS_DEVICE_LIMIT_EXCEEDED 20 -typedef double (*_cb_arduino_get_double)(int channelNumber, - double current_value); -typedef void (*_cb_arduino_get_temperature_and_humidity)(int channelNumber, - double *temp, - double *humidity); typedef void (*_cb_arduino_get_rgbw_value)(int channelNumber, unsigned char *red, unsigned char *green, @@ -149,20 +144,14 @@ class SuplaDeviceClass { bool isInitialized(bool msg); void setString(char *dst, const char *src, int max_size); int addChannel(int pin1, int pin2, bool hiIsLo, bool bistable); - void channelValueChanged(int channel_number, char v, double d, char var); void channelSetValue(int channel, char value, _supla_int_t DurationMS); - void channelSetDoubleValue(int channelNum, double value); - void setDoubleValue(char value[SUPLA_CHANNELVALUE_SIZE], double v); - bool addDHT(int Type); - void channelSetTempAndHumidityValue(int channelNum, - double temp, - double humidity); void setRGBWvalue(int channelNum, char value[SUPLA_CHANNELVALUE_SIZE]); void channelSetRGBWvalue(int channel, char value[SUPLA_CHANNELVALUE_SIZE]); SuplaDeviceParams Params; SuplaChannelPin *channel_pin; int channel_pin_count; + int port; int rs_count; SuplaDeviceRollerShutter *roller_shutter; @@ -214,14 +203,6 @@ class SuplaDeviceClass { TDS_SuplaDeviceChannel_C *channel, unsigned long time_diff, int channel_idx); - void iterate_sensor(SuplaChannelPin *pin, - TDS_SuplaDeviceChannel_C *channel, - unsigned long time_diff, - int channel_idx); - void iterate_thermometer(SuplaChannelPin *pin, - TDS_SuplaDeviceChannel_C *channel, - unsigned long time_diff, - int channel_idx); void iterate_rollershutter(SuplaDeviceRollerShutter *rs, SuplaChannelPin *pin, TDS_SuplaDeviceChannel_C *channel); @@ -230,10 +211,6 @@ class SuplaDeviceClass { unsigned long time_diff, int channel_number); - void begin_thermometer(SuplaChannelPin *pin, - TDS_SuplaDeviceChannel_C *channel, - int channel_number); - private: bool suplaDigitalRead_isHI(int channelNumber, uint8_t pin); void suplaDigitalWrite_setHI(int channelNumber, uint8_t pin, bool hi); @@ -244,7 +221,6 @@ class SuplaDeviceClass { ~SuplaDeviceClass(); void channelValueChanged(int channel_number, char v); - void channelDoubleValueChanged(int channel_number, double v); bool begin(char GUID[SUPLA_GUID_SIZE], const char *Server, @@ -267,8 +243,6 @@ class SuplaDeviceClass { void setRollerShutterButtons(int channel_number, int btnUpPin, int btnDownPin); - bool addSensorNO(int sensorPin, bool pullUp); - bool addSensorNO(int sensorPin); bool addRgbControllerAndDimmer(void); bool addRgbController(void); bool addDimmer(void); @@ -308,6 +282,7 @@ class SuplaDeviceClass { void setStatusFuncImpl(_impl_arduino_status impl_arduino_status); void setTimerFuncImpl(_impl_arduino_timer impl_arduino_timer); + void setServerPort(int value); void onVersionError(TSDC_SuplaVersionError *version_error); void onRegisterResult(TSD_SuplaRegisterDeviceResult *register_device_result); diff --git a/libraries/SuplaDevice/examples/Supla_DallasTemperature/Supla_DallasTemperature.ino b/libraries/SuplaDevice/examples/Supla_DallasTemperature/Supla_DallasTemperature.ino index 45056ee1..a6e5b3df 100644 --- a/libraries/SuplaDevice/examples/Supla_DallasTemperature/Supla_DallasTemperature.ino +++ b/libraries/SuplaDevice/examples/Supla_DallasTemperature/Supla_DallasTemperature.ino @@ -74,15 +74,6 @@ void setup() { SuplaDevice.addRollerShutterRelays(46, // 46 - Pin number where the 1st relay is connected 47, true); // 47 - Pin number where the 2nd relay is connected - // CHANNEL4 - Opening sensor (Normal Open) - SuplaDevice.addSensorNO(5); // 5 - Pin number where the sensor is connected - // Call SuplaDevice.addSensorNO(A0, true) with an extra "true" parameter - // to enable the internal pull-up resistor - - - // CHANNEL5 - Opening sensor (Normal Open) - SuplaDevice.addSensorNO(6); // 6 - Pin number where the sensor is connected - // CHANNEL6-9 - Thermometer DS18B20 // 4 DS18B20 thermometers at pin 23. DS address can be omitted when there is only one device at a pin diff --git a/libraries/SuplaDevice/examples/Supla_DallasTemperature_No_SSL/Supla_DallasTemperature_No_SSL.ino b/libraries/SuplaDevice/examples/Supla_DallasTemperature_No_SSL/Supla_DallasTemperature_No_SSL.ino index 92b36f23..dae058d4 100644 --- a/libraries/SuplaDevice/examples/Supla_DallasTemperature_No_SSL/Supla_DallasTemperature_No_SSL.ino +++ b/libraries/SuplaDevice/examples/Supla_DallasTemperature_No_SSL/Supla_DallasTemperature_No_SSL.ino @@ -44,30 +44,6 @@ void setup() { * Otherwise you will get "Channel conflict!" error. */ - // CHANNEL0 - RELAY - SuplaDevice.addRelay(44, true); // 44 - Pin number where the relay is connected - // Call SuplaDevice.addRelay(44, true) with an extra "true" parameter - // to enable "port value inversion" - // where HIGH == LOW, and LOW == HIGH - - // CHANNEL1 - RELAY - SuplaDevice.addRelay(45, true); // 45 - Pin number where the relay is connected - - // CHANNEL3 - TWO RELAYS (Roller shutter operation) - SuplaDevice.addRollerShutterRelays(46, // 46 - Pin number where the 1st relay is connected - 47, true); // 47 - Pin number where the 2nd relay is connected - - // CHANNEL4 - Opening sensor (Normal Open) - SuplaDevice.addSensorNO(5); // 5 - Pin number where the sensor is connected - // Call SuplaDevice.addSensorNO(A0, true) with an extra "true" parameter - // to enable the internal pull-up resistor - - - // CHANNEL5 - Opening sensor (Normal Open) - SuplaDevice.addSensorNO(6); // 6 - Pin number where the sensor is connected - - - // CHANNEL6-9 - Thermometer DS18B20 // 4 DS18B20 thermometers at pin 23. DS address can be omitted when there is only one device at a pin DeviceAddress ds1addr = {0x28, 0xFF, 0xC8, 0xAB, 0x6E, 0x18, 0x01, 0xFC}; DeviceAddress ds2addr = {0x28, 0xFF, 0x54, 0x73, 0x6E, 0x18, 0x01, 0x77}; diff --git a/libraries/SuplaDevice/network.cpp b/libraries/SuplaDevice/network.cpp index 10db0992..a8fb40c3 100644 --- a/libraries/SuplaDevice/network.cpp +++ b/libraries/SuplaDevice/network.cpp @@ -14,13 +14,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include "supla/network/network.h" - #include #include "SuplaDevice.h" #include "supla-common/log.h" #include "supla-common/srpc.h" +#include "supla/element.h" +#include "supla/network/network.h" namespace Supla { @@ -57,10 +57,25 @@ void message_received(void *_srpc, ((SuplaDeviceClass *)_sdc) ->onRegisterResult(rd.data.sd_register_device_result); break; - case SUPLA_SD_CALL_CHANNEL_SET_VALUE: - ((SuplaDeviceClass *)_sdc) - ->channelSetValueByServer(rd.data.sd_channel_new_value); + case SUPLA_SD_CALL_CHANNEL_SET_VALUE: { + auto element = Supla::Element::getElementByChannelNumber( + rd.data.sd_channel_new_value->ChannelNumber); + if (element) { + int actionResult = + element->handleNewValueFromServer(rd.data.sd_channel_new_value); + if (actionResult != -1) { + srpc_ds_async_set_channel_result( + _srpc, + rd.data.sd_channel_new_value->ChannelNumber, + rd.data.sd_channel_new_value->SenderID, + actionResult); + } + } else { + ((SuplaDeviceClass *)_sdc) + ->channelSetValueByServer(rd.data.sd_channel_new_value); + } break; + } case SUPLA_SDC_CALL_SET_ACTIVITY_TIMEOUT_RESULT: ((SuplaDeviceClass *)_sdc) ->channelSetActivityTimeoutResult( diff --git a/libraries/SuplaDevice/supla/channel.h b/libraries/SuplaDevice/supla/channel.h index 48b5208a..9ecbceeb 100644 --- a/libraries/SuplaDevice/supla/channel.h +++ b/libraries/SuplaDevice/supla/channel.h @@ -125,6 +125,33 @@ class Channel { } } + void setNewValue(int value) { + char newValue[SUPLA_CHANNELVALUE_SIZE]; + + memset(newValue, 0, SUPLA_CHANNELVALUE_SIZE); + + memcpy(newValue, &value, sizeof(int)); + if (setNewValue(newValue)) { + supla_log(LOG_DEBUG, + "Channel(%d) value changed to %d", + channelNumber, + value); + } + } + void setNewValue(bool value) { + char newValue[SUPLA_CHANNELVALUE_SIZE]; + + memset(newValue, 0, SUPLA_CHANNELVALUE_SIZE); + + newValue[0] = value; + if (setNewValue(newValue)) { + supla_log(LOG_DEBUG, + "Channel(%d) value changed to %d", + channelNumber, + value); + } + } + void setNewValue(TElectricityMeter_ExtendedValue &emValue) { // Prepare standard channel value if (sizeof(TElectricityMeter_Value) <= SUPLA_CHANNELVALUE_SIZE) { @@ -192,6 +219,12 @@ class Channel { } } + void setFuncList(int functions) { + if (channelNumber >= 0) { + reg_dev.channels[channelNumber].FuncList = functions; + } + } + int getChannelNumber() { return channelNumber; } diff --git a/libraries/SuplaDevice/supla/control/bistable_relay.h b/libraries/SuplaDevice/supla/control/bistable_relay.h new file mode 100644 index 00000000..e1626550 --- /dev/null +++ b/libraries/SuplaDevice/supla/control/bistable_relay.h @@ -0,0 +1,155 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* BistableRelay + * This class can be used to controll bistable relay. + * Supla device will send short impulses (<0.5 s) on GPIO to toggle bistable + * relay state. + * Device does not have knowledge about the status of bistable relay, so it + * has to be read on a different GPIO (statusPin) + * This class can work without statusPin information, but Supla will lose + * information about status of bistable relay. + */ + +#ifndef _bistable_relay_h +#define _bistable_relay_h + +#include "relay.h" + +namespace Supla { +namespace Control { +class BistableRelay : public Relay { + public: + BistableRelay(int pin, + int statusPin = -1, + bool statusPullUp = true, + bool statusHighIsOn = true, + bool highIsOn = true, + _supla_int_t functions = + (0xFF ^ SUPLA_BIT_FUNC_CONTROLLINGTHEROLLERSHUTTER)) + : Relay(pin, highIsOn, functions), + statusPin(statusPin), + statusPullUp(statusPullUp), + statusHighIsOn(statusHighIsOn), + disarmTimeMs(0), + busy(false) { + } + + void onInit() { + Relay::onInit(); + if (statusPin >= 0) { + pinMode(statusPin, statusPullUp ? INPUT_PULLUP : INPUT); + channel.setNewValue(isOn()); + } else { + channel.setNewValue(false); + } + } + + void iterateAlways() { + Relay::iterateAlways(); + + if (statusPin >= 0 && (lastReadTime + 100 < millis())) { + lastReadTime = millis(); + channel.setNewValue(isOn()); + } + + if (disarmTimeMs < millis()) { + busy = false; + Supla::Io::digitalWrite(channel.getChannelNumber(), pin, pinOffValue()); + } + } + + int handleNewValueFromServer(TSD_SuplaChannelNewValue *newValue) { + // ignore new requests if we are in the middle of state change + if (busy) { + return 0; + } else { + return Relay::handleNewValueFromServer(newValue); + } + } + + virtual void turnOn(_supla_int_t duration) { + if (busy) { + return; + } + + // Change turn on requests duration to be at least 1 s + if (duration > 0 && duration < 1000) { + duration = 1000; + } + if (duration > 0) { + durationMs = duration + millis(); + } + + if (isStatusUnknown()) { + internalToggle(); + } else if (!isOn()) { + internalToggle(); + } + } + + virtual void turnOff(_supla_int_t duration) { + if (busy) { + return; + } + + durationMs = 0; + + if (isStatusUnknown()) { + internalToggle(); + } else if (isOn()) { + internalToggle(); + } + } + + virtual bool isOn() { + if (isStatusUnknown()) { + return false; + } + return Supla::Io::digitalRead(channel.getChannelNumber(), statusPin) == (statusHighIsOn ? HIGH : LOW); + } + + bool isStatusUnknown() { + return (statusPin < 0); + } + + virtual void toggle() { + if (busy) { + return; + } + durationMs = 0; + + internalToggle(); + } + + protected: + void internalToggle() { + busy = true; + disarmTimeMs = millis() + 200; + Supla::Io::digitalWrite(channel.getChannelNumber(), pin, pinOnValue()); + } + + int statusPin; + bool statusPullUp; + bool statusHighIsOn; + unsigned long disarmTimeMs; + bool busy; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/libraries/SuplaDevice/supla/control/button.h b/libraries/SuplaDevice/supla/control/button.h new file mode 100644 index 00000000..9e116fea --- /dev/null +++ b/libraries/SuplaDevice/supla/control/button.h @@ -0,0 +1,114 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _button_h +#define _button_h + +#include + +#include "../element.h" +#include "../will_trigger.h" + +namespace Supla { +namespace Control { +class Button : public Element, public WillTrigger { + public: + enum Event { + ON_PRESS, // Triggered on transition to valueOnPress() + ON_RELEASE, // Triggered on transition from valueOnPress() + ON_CHANGE // Triggered on all transitions + }; + + Button(int pin, bool pullUp = false, bool invertLogic = false) + : pin(pin), + pullUp(pullUp), + prevStatus(LOW), + newStatusCandidate(LOW), + debounceTimeMs(0), + filterTimeMs(0), + debounceDelayMs(50), + swNoiseFilterDelayMs(20), + invertLogic(invertLogic) { + } + + void iterateAlways() { + // Ignore anything that happen within debounceDelayMs ms since last state + // change + if (millis() - debounceTimeMs > debounceDelayMs) { + int currentStatus = digitalRead(pin); + if (currentStatus != prevStatus) { + // If status is changed, then make sure that it will be kept at + // least swNoiseFilterDelayMs ms to avoid noise + if (currentStatus != newStatusCandidate) { + newStatusCandidate = currentStatus; + filterTimeMs = millis(); + return; + } + // If new status is kept at least swNoiseFilterDelayMs ms, then apply + // change of status + if (millis() - filterTimeMs > swNoiseFilterDelayMs) { + debounceTimeMs = millis(); + prevStatus = currentStatus; + if (currentStatus == valueOnPress()) { + runTrigger(ON_PRESS); + runTrigger(ON_CHANGE); + } else { + runTrigger(ON_RELEASE); + runTrigger(ON_CHANGE); + } + } + } else { + // If current status is the same as prevStatus, then reset + // new status candidate + newStatusCandidate = prevStatus; + } + } + } + + void onInit() { + pinMode(pin, pullUp ? INPUT_PULLUP : INPUT); + prevStatus = digitalRead(pin); + newStatusCandidate = prevStatus; + } + + int valueOnPress() { + return invertLogic ? LOW : HIGH; + } + + void setSwNoiseFilterDelay(int newDelayMs) { + swNoiseFilterDelayMs = newDelayMs; + } + + void setDebounceDelay(int newDelayMs) { + debounceDelayMs = newDelayMs; + } + + protected: + int pin; + bool pullUp; + unsigned long debounceTimeMs; + unsigned long filterTimeMs; + int debounceDelayMs; + int swNoiseFilterDelayMs; + int prevStatus; + int newStatusCandidate; + bool invertLogic; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/libraries/SuplaDevice/supla/control/relay.h b/libraries/SuplaDevice/supla/control/relay.h new file mode 100644 index 00000000..14b30c74 --- /dev/null +++ b/libraries/SuplaDevice/supla/control/relay.h @@ -0,0 +1,146 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* Relay class + * This class is used to control any type of relay that can be controlled + * by setting LOW or HIGH output on selected GPIO. + */ + +#ifndef _relay_h +#define _relay_h + +#include + +#include "../../io.h" +#include "../channel.h" +#include "../element.h" +#include "../triggerable.h" + +namespace Supla { +namespace Control { +class Relay : public Element, public Triggerable { + public: + enum Action { TURN_ON, TURN_OFF, TOGGLE }; + + Relay(int pin, + bool highIsOn = true, + _supla_int_t functions = (0xFF ^ + SUPLA_BIT_FUNC_CONTROLLINGTHEROLLERSHUTTER)) + : pin(pin), durationMs(0), highIsOn(highIsOn) { + channel.setType(SUPLA_CHANNELTYPE_RELAY); + channel.setFuncList(functions); + } + + virtual uint8_t pinOnValue() { + return highIsOn ? HIGH : LOW; + } + + virtual uint8_t pinOffValue() { + return highIsOn ? LOW : HIGH; + } + + void onInit() { + pinMode(pin, OUTPUT); + Supla::Io::digitalWrite(channel.getChannelNumber(), pin, pinOffValue()); + } + + void iterateAlways() { + if (durationMs && durationMs < millis()) { + durationMs = 0; + toggle(); + } + } + + int handleNewValueFromServer(TSD_SuplaChannelNewValue *newValue) { + int result = -1; + if (newValue->value[0] == 1) { + turnOn(newValue->DurationMS); + result = 1; + } else if (newValue->value[0] == 0) { + turnOff(newValue->DurationMS); + result = 1; + } + + return result; + } + + virtual void turnOn(_supla_int_t duration = 0) { + if (duration > 0) { + durationMs = duration + millis(); + } + Supla::Io::digitalWrite(channel.getChannelNumber(), pin, pinOnValue()); + + channel.setNewValue(true); + } + + virtual void turnOff(_supla_int_t duration = 0) { + durationMs = 0; + Supla::Io::digitalWrite(channel.getChannelNumber(), pin, pinOffValue()); + + channel.setNewValue(false); + } + + virtual bool isOn() { + return Supla::Io::digitalRead(channel.getChannelNumber(), pin) == + pinOnValue(); + } + + virtual void toggle() { + durationMs = 0; + Supla::Io::digitalWrite( + channel.getChannelNumber(), + pin, + Supla::Io::digitalRead(channel.getChannelNumber(), pin) == LOW ? HIGH + : LOW); + + if (isOn()) { + channel.setNewValue(true); + } else { + channel.setNewValue(false); + } + } + + void trigger(int trigger, int action) { + switch (action) { + case TURN_ON: { + turnOn(); + break; + } + case TURN_OFF: { + turnOff(); + break; + } + case TOGGLE: { + toggle(); + break; + } + } + } + + protected: + Channel *getChannel() { + return &channel; + } + Channel channel; + _supla_int_t durationMs; + int pin; + bool highIsOn; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/libraries/SuplaDevice/supla/control/rgbw_base.h b/libraries/SuplaDevice/supla/control/rgbw_base.h new file mode 100644 index 00000000..bf3f3bda --- /dev/null +++ b/libraries/SuplaDevice/supla/control/rgbw_base.h @@ -0,0 +1,88 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _rgbw_base_h +#define _rgbw_base_h + +#include + +#include "../../io.h" +#include "../channel.h" +#include "../element.h" +#include "../triggerable.h" + +namespace Supla { +namespace Control { +class RGBWBase : public Element, public Triggerable { + public: + enum Action { TURN_ON, TURN_OFF, TOGGLE }; + + virtual void setRGBWValueOnDevice(uint8_t red, + uint8_t green, + uint8_t blue, + int8_t colorBrightness, + int8_t brightness) = 0; + + int handleNewValueFromServer(TSD_SuplaChannelNewValue *newValue) { + uint8_t red = static_cast(newValue->value[4]); + uint8_t green = static_cast(newValue->value[3]); + uint8_t blue = static_cast(newValue->value[2]); + int8_t colorBrightness = static_cast(newValue->value[1]); + char brightness = static_cast(newValue->value[0]); + + setRGBWValueOnDevice(red, green, blue, colorBrightness, brightness); + + channel.setNewValue(newValue->value); + + return -1; + } + + virtual void turnOn(_supla_int_t duration = 0) = 0; + + virtual void turnOff(_supla_int_t duration = 0) = 0; + + virtual bool isOn() = 0; + + virtual void toggle() = 0; + + void trigger(int trigger, int action) { + switch (action) { + case TURN_ON: { + turnOn(); + break; + } + case TURN_OFF: { + turnOff(); + break; + } + case TOGGLE: { + toggle(); + break; + } + } + } + + protected: + Channel *getChannel() { + return &channel; + } + Channel channel; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/libraries/SuplaDevice/supla/control/virtual_relay.h b/libraries/SuplaDevice/supla/control/virtual_relay.h new file mode 100644 index 00000000..106da993 --- /dev/null +++ b/libraries/SuplaDevice/supla/control/virtual_relay.h @@ -0,0 +1,66 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _virtual_relay_h +#define _virtual_relay_h + +#include "relay.h" + +namespace Supla { +namespace Control { +class VirtualRelay : public Relay { + public: + VirtualRelay(_supla_int_t functions = (0xFF ^ SUPLA_BIT_FUNC_CONTROLLINGTHEROLLERSHUTTER)) : Relay(-1, true, functions), state(false) { + } + + void onInit() { + } + + void turnOn(_supla_int_t duration) { + if (duration > 0) { + durationMs = duration + millis(); + } + state = true; + + channel.setNewValue(state); + } + + virtual void turnOff(_supla_int_t duration) { + durationMs = 0; + state = false; + + channel.setNewValue(state); + } + + virtual bool isOn() { + return state; + } + + virtual void toggle() { + durationMs = 0; + state = !state; + + channel.setNewValue(isOn()); + } + + protected: + bool state; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/libraries/SuplaDevice/supla/element.h b/libraries/SuplaDevice/supla/element.h index e2aa04f7..6e222add 100644 --- a/libraries/SuplaDevice/supla/element.h +++ b/libraries/SuplaDevice/supla/element.h @@ -44,70 +44,102 @@ class ElementIterator { } */ class Element { - public: - - Element() { - if (firstPtr == nullptr) { - firstPtr = this; - } else { - last()->nextPtr = this; - } - nextPtr = nullptr; + public: + Element() : lastReadTime(0), nextPtr(nullptr) { + if (firstPtr == nullptr) { + firstPtr = this; + } else { + last()->nextPtr = this; } + } - static Element *begin() { - return firstPtr; - } + static Element *begin() { + return firstPtr; + } - static Element *last() { - Element *ptr = firstPtr; - while (ptr && ptr->nextPtr) { - ptr = ptr->nextPtr; - } - return ptr; + static Element *last() { + Element *ptr = firstPtr; + while (ptr && ptr->nextPtr) { + ptr = ptr->nextPtr; } + return ptr; + } - Element *next() { - return nextPtr; + static Element *getElementByChannelNumber(int channelNumber) { + Element *element = begin(); + while (element != nullptr && element->getChannelNumber() != channelNumber) { + element = element->next(); } - // method called during SuplaDevice initialization. I.e. load initial state, initialize pins etc. - virtual void onInit() {}; - - // TODO: - // method called during Config initialization (i.e. read from EEPROM, FRAM). Called only if Config module is - // configured - virtual void onLoadConfig() { }; - - // method called on each SuplaDevice iteration (before Network layer iteration). When Device is connected, - // both iterateAlways() and iterateConnected() are called. - virtual void iterateAlways() {}; - - // method called on each Supla::Device iteration when Device is connected and registered to Supla server - virtual bool iterateConnected(void *srpc) { - Channel *channel = getChannel(); - if (channel && channel->isUpdateReady() && channel->nextCommunicationTimeMs < millis()) { - channel->nextCommunicationTimeMs = millis() + 100; - channel->sendUpdate(srpc); - return false; - } - return true; + return element; + } + + Element *next() { + return nextPtr; + } + + // method called during SuplaDevice initialization. I.e. load initial state, + // initialize pins etc. + virtual void onInit(){}; + + // TODO: + // method called during Config initialization (i.e. read from EEPROM, FRAM). + // Called only if Config module is configured + virtual void onLoadConfig(){}; + + // method called on each SuplaDevice iteration (before Network layer + // iteration). When Device is connected, both iterateAlways() and + // iterateConnected() are called. + virtual void iterateAlways(){}; + + // method called on each Supla::Device iteration when Device is connected and + // registered to Supla server + virtual bool iterateConnected(void *srpc) { + Channel *channel = getChannel(); + if (channel && channel->isUpdateReady() && + channel->nextCommunicationTimeMs < millis()) { + channel->nextCommunicationTimeMs = millis() + 100; + channel->sendUpdate(srpc); + return false; } - - // method called on timer interupt - // Include all actions that have to be executed periodically regardless of other SuplaDevice activities - virtual void onTimer() { }; - - // method called on fast timer interupt - // Include all actions that have to be executed periodically regardless of other SuplaDevice activities - virtual void onFastTimer() { }; - - protected: - virtual Channel *getChannel() { - return nullptr; + return true; + } + + // method called on timer interupt + // Include all actions that have to be executed periodically regardless of + // other SuplaDevice activities + virtual void onTimer(){}; + + // method called on fast timer interupt + // Include all actions that have to be executed periodically regardless of + // other SuplaDevice activities + virtual void onFastTimer(){}; + + // return value: + // -1 - don't send reply to server + // 0 - success==false + // 1 - success==true + virtual int handleNewValueFromServer(TSD_SuplaChannelNewValue *newValue) { + return -1; + } + + int getChannelNumber() { + int result = -1; + Channel *channel = getChannel(); + if (channel) { + result = channel->getChannelNumber(); } - static Element *firstPtr;; - Element *nextPtr; + return result; + } + + protected: + virtual Channel *getChannel() { + return nullptr; + } + static Element *firstPtr; + ; + Element *nextPtr; + unsigned long lastReadTime; }; }; // namespace Supla diff --git a/libraries/SuplaDevice/supla/network/ENC28J60.h b/libraries/SuplaDevice/supla/network/ENC28J60.h index 36ba5a1c..a0319c75 100644 --- a/libraries/SuplaDevice/supla/network/ENC28J60.h +++ b/libraries/SuplaDevice/supla/network/ENC28J60.h @@ -65,8 +65,12 @@ class ENC28J60 : public Supla::Network { return sendSize; } - bool connect(const char *server, int port) { - return client.connect(server, port); + bool connect(const char *server, int port = -1) { + int connectionPort = (port == -1 ? 2015 : port); + supla_log( + LOG_DEBUG, "Establishing connection with: %s (port: %d)", server, connectionPort); + + return client.connect(server, connectionPort); } bool connected() { diff --git a/libraries/SuplaDevice/supla/network/esp32_wifi.h b/libraries/SuplaDevice/supla/network/esp32_wifi.h index ee585c7d..ef7ff044 100644 --- a/libraries/SuplaDevice/supla/network/esp32_wifi.h +++ b/libraries/SuplaDevice/supla/network/esp32_wifi.h @@ -72,10 +72,11 @@ class ESP32Wifi : public Supla::Network { return sendSize; } - bool connect(const char *server, int port) { + bool connect(const char *server, int port = -1) { + int connectionPort = (port == -1 ? 2015 : port); supla_log( - LOG_DEBUG, "Establishing connection with: %s (port: %d)", server, port); - return client.connect(server, port); + LOG_DEBUG, "Establishing connection with: %s (port: %d)", server, connectionPort); + return client.connect(server, connectionPort); } bool connected() { diff --git a/libraries/SuplaDevice/supla/network/esp_wifi.h b/libraries/SuplaDevice/supla/network/esp_wifi.h index 6ec3507e..b9cc1511 100644 --- a/libraries/SuplaDevice/supla/network/esp_wifi.h +++ b/libraries/SuplaDevice/supla/network/esp_wifi.h @@ -39,7 +39,6 @@ class ESPWifi : public Supla::Network { client = nullptr; isSecured = true; - this->port = 2016; strcpy(ssid, wifiSsid); strcpy(password, wifiPassword); @@ -78,7 +77,7 @@ class ESPWifi : public Supla::Network { return sendSize; } - bool connect(const char *server, int port) { + bool connect(const char *server, int port = -1) { String message; if (client == NULL) { if (isSecured) { @@ -97,13 +96,18 @@ class ESPWifi : public Supla::Network { } } + int connectionPort = (isSecured ? 2016 : 2015); + if (port != -1) { + connectionPort = port; + } + supla_log(LOG_DEBUG, "Establishing %s with: %s (port: %d)", message.c_str(), server, - this->port); + connectionPort); - bool result = client->connect(server, this->port); + bool result = client->connect(server, connectionPort); if (result && isSecured) { if (!((WiFiClientSecure *)client)->verify(fingerprint.c_str(), server)) { @@ -159,21 +163,16 @@ class ESPWifi : public Supla::Network { } void enableSSL(bool value) { - this->isSecured = value; - } - - void setServerPort(int value) { - this->port = value; + isSecured = value; } void setServersCertFingerprint(String value) { - this->fingerprint = value; + fingerprint = value; } protected: WiFiClient *client = NULL; bool isSecured; - int port; String fingerprint; char ssid[MAX_SSID_SIZE]; char password[MAX_WIFI_PASSWORD_SIZE]; @@ -181,4 +180,4 @@ class ESPWifi : public Supla::Network { }; // namespace Supla -#endif \ No newline at end of file +#endif diff --git a/libraries/SuplaDevice/supla/network/ethernet_shield.h b/libraries/SuplaDevice/supla/network/ethernet_shield.h index f1eabd43..07ca043d 100644 --- a/libraries/SuplaDevice/supla/network/ethernet_shield.h +++ b/libraries/SuplaDevice/supla/network/ethernet_shield.h @@ -64,11 +64,12 @@ class EthernetShield : public Supla::Network { return sendSize; } - bool connect(const char *server, int port) { + bool connect(const char *server, int port = -1) { + int connectionPort = (port == -1 ? 2015 : port); supla_log( - LOG_DEBUG, "Establishing connection with: %s (port: %d)", server, port); + LOG_DEBUG, "Establishing connection with: %s (port: %d)", server, connectionPort); - return client.connect(server, port); + return client.connect(server, connectionPort); } bool connected() { diff --git a/libraries/SuplaDevice/supla/network/network.h b/libraries/SuplaDevice/supla/network/network.h index 7a047194..6187088b 100644 --- a/libraries/SuplaDevice/supla/network/network.h +++ b/libraries/SuplaDevice/supla/network/network.h @@ -50,7 +50,7 @@ class Network { return -1; } - static bool Connect(const char *server, int port) { + static bool Connect(const char *server, int port = -1) { if (Instance() != NULL) { Instance()->clearTimeCounters(); return Instance()->connect(server, port); @@ -101,7 +101,7 @@ class Network { Network(IPAddress *ip); virtual int read(void *buf, int count) = 0; virtual int write(void *buf, int count) = 0; - virtual bool connect(const char *server, int port) = 0; + virtual bool connect(const char *server, int port = -1) = 0; virtual bool connected() = 0; virtual void disconnect() = 0; virtual void setup() = 0; diff --git a/libraries/SuplaDevice/supla/sensor/DS18B20.h b/libraries/SuplaDevice/supla/sensor/DS18B20.h index b894ea77..5a547942 100644 --- a/libraries/SuplaDevice/supla/sensor/DS18B20.h +++ b/libraries/SuplaDevice/supla/sensor/DS18B20.h @@ -106,7 +106,6 @@ class DS18B20 : public Thermometer { DS18B20(uint8_t pin, uint8_t *deviceAddress = nullptr) { OneWireBus *bus = oneWireBus; OneWireBus *prevBus = nullptr; - lastReadTime = 0; address[0] = 0; lastValidValue = TEMPERATURE_NOT_AVAILABLE; retryCounter = 0; diff --git a/libraries/SuplaDevice/supla/sensor/HC_SR04.h b/libraries/SuplaDevice/supla/sensor/HC_SR04.h index 107d3a91..29f8e2f7 100644 --- a/libraries/SuplaDevice/supla/sensor/HC_SR04.h +++ b/libraries/SuplaDevice/supla/sensor/HC_SR04.h @@ -20,35 +20,50 @@ #include "supla/channel.h" #include "supla/sensor/distance.h" +#define DURATION_COUNT 2 + namespace Supla { namespace Sensor { -class HC_SR04: public Distance { +class HC_SR04 : public Distance { public: - HC_SR04(int8_t trigPin,int8_t echoPin) { - _trigPin = trigPin; - _echoPin = echoPin; + HC_SR04(int8_t trigPin, int8_t echoPin) + : failCount(0), lastDuration(0) { + _trigPin = trigPin; + _echoPin = echoPin; } void onInit() { - pinMode(_trigPin, OUTPUT); - pinMode(_echoPin, INPUT); + pinMode(_trigPin, OUTPUT); + pinMode(_echoPin, INPUT); + digitalWrite(_trigPin, LOW); + delayMicroseconds(2); + + channel.setNewValue(getValue()); channel.setNewValue(getValue()); } - + virtual double getValue() { - double duration; - digitalWrite(_trigPin, LOW); - delayMicroseconds(2); digitalWrite(_trigPin, HIGH); delayMicroseconds(10); digitalWrite(_trigPin, LOW); - duration = pulseIn(_echoPin, HIGH); - return duration*0.034/2/100; + unsigned long duration = pulseIn(_echoPin, HIGH, 60000); + if (duration > 50) { + lastDuration = duration; + failCount = 0; + } else { + duration = lastDuration; + failCount++; + } + + return failCount <= 3 ? duration * 0.034 / 2 / 100 + : DISTANCE_NOT_AVAILABLE; } protected: int8_t _trigPin; int8_t _echoPin; - + char failCount; + bool ready; + unsigned long lastDuration; }; }; // namespace Sensor diff --git a/libraries/SuplaDevice/supla/sensor/binary.h b/libraries/SuplaDevice/supla/sensor/binary.h new file mode 100644 index 00000000..126d2914 --- /dev/null +++ b/libraries/SuplaDevice/supla/sensor/binary.h @@ -0,0 +1,62 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _binary_h +#define _binary_h + +#include "../channel.h" +#include "../element.h" +#include +#include + +namespace Supla { +namespace Sensor { +class Binary: public Element { + public: + Binary(int pin, bool pullUp = false) : pin(pin), pullUp(pullUp) { + channel.setType(SUPLA_CHANNELTYPE_SENSORNO); + } + + bool getValue() { + return Supla::Io::digitalRead(channel.getChannelNumber(), pin) == LOW ? false : true; + } + + void iterateAlways() { + if (lastReadTime + 100 < millis()) { + lastReadTime = millis(); + channel.setNewValue(getValue()); + } + } + + void onInit() { + pinMode(pin, pullUp ? INPUT_PULLUP : INPUT); + channel.setNewValue(getValue()); + } + + + protected: + Channel *getChannel() { + return &channel; + } + Channel channel; + int pin; + bool pullUp; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/libraries/SuplaDevice/supla/sensor/distance.h b/libraries/SuplaDevice/supla/sensor/distance.h index 542ef66e..a88a4b6c 100644 --- a/libraries/SuplaDevice/supla/sensor/distance.h +++ b/libraries/SuplaDevice/supla/sensor/distance.h @@ -37,7 +37,7 @@ class Distance: public Element { } void iterateAlways() { - if (lastReadTime + 10000 < millis()) { + if (lastReadTime + 500 < millis()) { lastReadTime = millis(); channel.setNewValue(getValue()); } @@ -48,7 +48,6 @@ class Distance: public Element { Channel *getChannel() { return &channel; } - unsigned long lastReadTime; Channel channel; }; diff --git a/libraries/SuplaDevice/supla/sensor/electricity_meter.h b/libraries/SuplaDevice/supla/sensor/electricity_meter.h index 9ced0498..c178f121 100644 --- a/libraries/SuplaDevice/supla/sensor/electricity_meter.h +++ b/libraries/SuplaDevice/supla/sensor/electricity_meter.h @@ -26,7 +26,7 @@ namespace Supla { namespace Sensor { class ElectricityMeter : public Element { public: - ElectricityMeter() : lastReadTime(0), valueChanged(false) { + ElectricityMeter() : valueChanged(false) { extChannel.setType(SUPLA_CHANNELTYPE_ELECTRICITY_METER); extChannel.setDefault(SUPLA_CHANNELFNC_ELECTRICITY_METER); memset(&emValue, 0, sizeof(emValue)); @@ -241,7 +241,6 @@ class ElectricityMeter : public Element { _supla_int_t rawCurrent[MAX_PHASES]; bool valueChanged; bool currentMeasurementAvailable; - unsigned long lastReadTime; }; }; // namespace Sensor diff --git a/libraries/SuplaDevice/supla/sensor/normally_open.h b/libraries/SuplaDevice/supla/sensor/normally_open.h new file mode 100644 index 00000000..bfa605ac --- /dev/null +++ b/libraries/SuplaDevice/supla/sensor/normally_open.h @@ -0,0 +1,33 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _normally_open_h +#define _normally_open_h + +#include "binary.h" + +namespace Supla { +namespace Sensor { +class NormallyOpen : public Binary { + public: + NormallyOpen(int pin, bool pullUp = false) : Binary(pin, pullUp) { + } +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/libraries/SuplaDevice/supla/sensor/pressure.h b/libraries/SuplaDevice/supla/sensor/pressure.h index 9e3ed9ce..634b3af4 100644 --- a/libraries/SuplaDevice/supla/sensor/pressure.h +++ b/libraries/SuplaDevice/supla/sensor/pressure.h @@ -48,7 +48,6 @@ class Pressure : public Element { Channel *getChannel() { return &channel; } - unsigned long lastReadTime; Channel channel; }; diff --git a/libraries/SuplaDevice/supla/sensor/rain.h b/libraries/SuplaDevice/supla/sensor/rain.h index 49ca2d19..cdddb178 100644 --- a/libraries/SuplaDevice/supla/sensor/rain.h +++ b/libraries/SuplaDevice/supla/sensor/rain.h @@ -48,7 +48,6 @@ class Rain: public Element { Channel *getChannel() { return &channel; } - unsigned long lastReadTime; Channel channel; }; diff --git a/libraries/SuplaDevice/supla/sensor/therm_hygro_meter.h b/libraries/SuplaDevice/supla/sensor/therm_hygro_meter.h index e03dd9fd..1dcc7cb6 100644 --- a/libraries/SuplaDevice/supla/sensor/therm_hygro_meter.h +++ b/libraries/SuplaDevice/supla/sensor/therm_hygro_meter.h @@ -30,11 +30,11 @@ class ThermHygroMeter: public Thermometer { channel.setDefault(SUPLA_CHANNELFNC_HUMIDITYANDTEMPERATURE); } - double getTemp() { + virtual double getTemp() { return TEMPERATURE_NOT_AVAILABLE; } - double getHumi() { + virtual double getHumi() { return HUMIDITY_NOT_AVAILABLE; } diff --git a/libraries/SuplaDevice/supla/sensor/thermometer.h b/libraries/SuplaDevice/supla/sensor/thermometer.h index ee540e8e..ce352566 100644 --- a/libraries/SuplaDevice/supla/sensor/thermometer.h +++ b/libraries/SuplaDevice/supla/sensor/thermometer.h @@ -47,7 +47,6 @@ class Thermometer : public Element { Channel *getChannel() { return &channel; } - unsigned long lastReadTime; Channel channel; }; diff --git a/libraries/SuplaDevice/supla/sensor/weight.h b/libraries/SuplaDevice/supla/sensor/weight.h index 95d87a4d..ab814841 100644 --- a/libraries/SuplaDevice/supla/sensor/weight.h +++ b/libraries/SuplaDevice/supla/sensor/weight.h @@ -48,7 +48,6 @@ class Weight : public Element { Channel *getChannel() { return &channel; } - unsigned long lastReadTime; Channel channel; }; diff --git a/libraries/SuplaDevice/supla/sensor/wind.h b/libraries/SuplaDevice/supla/sensor/wind.h index 2ae9708b..edb0a4fd 100644 --- a/libraries/SuplaDevice/supla/sensor/wind.h +++ b/libraries/SuplaDevice/supla/sensor/wind.h @@ -48,7 +48,6 @@ class Wind: public Element { Channel *getChannel() { return &channel; } - unsigned long lastReadTime; Channel channel; }; diff --git a/libraries/SuplaDevice/supla/triggerable.h b/libraries/SuplaDevice/supla/triggerable.h new file mode 100644 index 00000000..1f88ea34 --- /dev/null +++ b/libraries/SuplaDevice/supla/triggerable.h @@ -0,0 +1,28 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _triggerable_h +#define _triggerable_h + +namespace Supla { +class Triggerable { + public: + virtual void trigger(int event, int action) = 0; +}; + +}; + +#endif diff --git a/libraries/SuplaDevice/supla/will_trigger.h b/libraries/SuplaDevice/supla/will_trigger.h new file mode 100644 index 00000000..e1c3406a --- /dev/null +++ b/libraries/SuplaDevice/supla/will_trigger.h @@ -0,0 +1,62 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _will_trigger_h +#define _will_trigger_h + +#include "triggerable.h" + +#define MAX_TRIGGERABLE_CLIENTS 10 + +namespace Supla { + +class TriggerableClient { + public: + Triggerable *client; + int onEvent; + int action; +}; + +class WillTrigger { + public: + WillTrigger() : registeredClientsCount(0) { + } + + virtual void willTrigger(Triggerable &client, int event, int action) { + if (registeredClientsCount < MAX_TRIGGERABLE_CLIENTS) { + clients[registeredClientsCount].client = &client; + clients[registeredClientsCount].onEvent = event; + clients[registeredClientsCount].action = action; + registeredClientsCount++; + } + } + + virtual void runTrigger(int event) { + for (int i = 0; i < registeredClientsCount; i++) { + if (clients[i].onEvent == event) { + clients[i].client->trigger(event, clients[i].action); + } + } + } + + protected: + TriggerableClient clients[MAX_TRIGGERABLE_CLIENTS]; + int registeredClientsCount; +}; + +}; + +#endif diff --git a/readme.md b/readme.md index 4967ba57..43a08a90 100644 --- a/readme.md +++ b/readme.md @@ -8,21 +8,35 @@ SuplaDevice is a library for [Arduino IDE](https://www.arduino.cc/en/main/softwa ## Hardware requirements -SuplaDevice works with Arduino Mega boards. Currently Arduino Uno is not supported. -It also works with ESP8266 based devices. +### Arduino Mega +SuplaDevice works with Arduino Mega boards. Currently Arduino Uno is not supported because RAM limitations. It should work on other Arduino boards with at least 8 kB of RAM. +Following network interfaces are supported: +* Ethernet Shield with W5100 chipset +* ENC28J60 (not recommended - see Supported hardware section) + +Warning: WiFi shields are currently not supported + +### ESP8266 +ESP8266 boards are supported. Network connection is done via internal WiFi. Tested with ESP8266 boards 2.6.3. +Most probably it will work with other ESP8266 compatible boards. + +### ESP32 +Experimental support for ESP32 boards is provided. Some issues seen with reconnection to WiFi router which requires further analysis. ## Installation -1. Download Arduino IDE, run it and configure it. Pplease refer to other tutorials how to start with Arduino IDE. -2. Make sure that your board is working by uploading example program from Arduino IDE. -... +Before you start, you will need to: +1. install Arduino IDE, +2. install support for your board +3. install driver for your USB to serial converter device (it can be external device, or build in on your board) +4. make sure that communication over serial interface with your board is working (i.e. check some example Arduino application) +5. download and install this librarary by copying SuplaDevice folder into your Arduino library folder -## Usage +Steps 1-4 are standard Arudino IDE setup procedures not related to SuplaDevice library. You can find many tutorials on Internet with detailed instructions. Tutorials doesn't have to be related in any way with Supla. + +After step 5 you should see Supla example applications in Arduino IDE examples. Select one and have fun! Example file requires adjustments before you compile them and upload to your board. Please read all comments in example and make proper adjustments. -### Supported hardware -SuplaDevice is created for Arduino Mega boards. Arduino Uno is not working because of RAM limitations. However it should be possible to run on Uno board as well, - if memory usage of library will be reduced. -Library also works with ESP8266 based boards. It was tested on Wemos D1 R1 board. +## Usage ### Network interfaces Supported network interfaces for Arduino Mega: @@ -33,16 +47,44 @@ or some other problem with network, program will stuck on initialization and wil Second warning: UIPEthernet library is consuming few hundred of bytes of RAM memory more, compared to standard Ethernet library. Supported network interface for ESP8266: -* There is native WIFI controller. Include `` and add `Supla::ESPWifi wifi(ssid, password);` as a global variable and provide SSID -and password in constructor. +* There is native WiFi controller. Include `` and add `Supla::ESPWifi wifi(ssid, password);` as a global variable and provide SSID and password in constructor. + +Supported network interface for ESP32: +* There is native WiFi controller. Include `` and add `Supla::ESP32Wifi wifi(ssid, password);` as a global variable and provide SSID and password in constructor. ### Exmaples -Each example can run on Arduino Mega or ESP8266 board. Please read comments in example files and uncomment proper library for your network interface. +Each example can run on Arduino Mega, ESP8266, or ESP32 board. Please read comments in example files and uncomment proper library for your network interface. SuplaSomfy, Supla_RollerShutter_FRAM - those examples are not updated yet. -### How to migrate programs written in SuplaDevice libraray versions 1.6 and older +### Folder structure + +* `supla-common` - Supla protocol definitions and structures. There are also methods to handle low level communication with Supla server, like message coding, decoding, sending and receiving. Those files are common with `supla-core` and the same code is run on multiple Supla platforms and services +* `supla/network` - implementation of network interfaces for supported boards +* `supla/sensor` - implementation of Supla sensor channels (thermometers, open/close sensors, etc.) +* `supla/control` - implementation of Supla control channels (various combinations of relays, buttons, action triggers) + +Some functions from above folders have dependencies to external libraries. Please check documentation included in header files. + +### How does it work? + +Everything that is visible in Supla (in Cloud, on API, mobile application) is called "channel". Supla channels are used to control relays, read temperature, check if garage door is open. + +SuplaDevice implements support for channels in `Channel` and `ChannelExtended` classes. Instances of those classes are part of objects called `Element` which are building blocks for any SuplaDevice application. + +All sensors, relays, buttons objects inherits from `Element` class. Each instance of such object will automatically register in SuplaDevice and proper virtual methods will be called by SuplaDevice in a specified way. + +All elements have to be constructed before `SuplaDevice.begin()` method is called. + +Supla channel number is assigned to each elemement with channel in an order of creation of objects. First channel will get number 0, second 1, etc. Supla server will not accept registration of device when order of channels is changed, or some channel is removed. In such case, you should remove device from Supla Cloud and register it again from scratch. + +`Element` class defines follwoing virtual methods that are called by SuplaDevice: +1. `onInit` - called within `SuplaDevice.begin()` method. It should: TODO... + +... + +## How to migrate programs written in SuplaDevice libraray versions 1.6 and older For Arduino Mega applications include proper network interface header: ```