From fa14c3f744fc360d028d2dde68a4930a8f10b942 Mon Sep 17 00:00:00 2001 From: Vitaliy D Date: Mon, 10 Jan 2022 20:52:31 +0100 Subject: [PATCH] Implement power control over radio transmission Other changes: * Always try to apply Temp/RH handlers * Decouple settings * Add test env * Static network config * CI: preinstall Git * Change max RH and RH hyst --- .circleci/config.yml | 10 +++-- include/Settings.h | 89 +++++++++++++++++++++++++++++++++++++++++++ include/version.h | 2 +- platformio.ini | 12 +++++- src/DHTDevice.cpp | 8 ++++ src/DHTDevice.h | 1 + src/Device.cpp | 8 ++++ src/Device.h | 15 +++++++- src/HTU2xDDevice.cpp | 9 ++++- src/HTU2xDDevice.h | 1 + src/PowerManager.cpp | 28 ++++++++++++-- src/PowerManager.h | 11 ++++++ src/main.cpp | 90 ++++++++++++++++++++++++-------------------- 13 files changed, 233 insertions(+), 51 deletions(-) create mode 100644 include/Settings.h diff --git a/.circleci/config.yml b/.circleci/config.yml index 3dfe6b7..6b5eec0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,6 +5,9 @@ jobs: docker: - image: alpine steps: + - run: + name: Install packages + command: apk add --no-cache curl docker-cli make openssh git - checkout - setup_remote_docker: version: 20.10.7 @@ -12,7 +15,6 @@ jobs: - run: name: smoke-test command: | - apk add --no-cache curl docker-cli make git if [ "$(git log -1 --format=format:%H -- client/)" == "$CIRCLE_SHA1" ] then make client-test @@ -25,12 +27,12 @@ jobs: docker: - image: alpine steps: + - run: + name: Install packages + command: apk add --no-cache docker-cli make openssh git - checkout - setup_remote_docker: version: 20.10.7 - - run: - name: Install packages - command: apk add --no-cache docker-cli make - run: name: Docker image release command: | diff --git a/include/Settings.h b/include/Settings.h new file mode 100644 index 0000000..76ac428 --- /dev/null +++ b/include/Settings.h @@ -0,0 +1,89 @@ +/** + * + * Settings.h + * + **/ + +/* + * !! WARNING !! + * + * !!! DO NOT USE pins 5(D1),4(D2) + * because they are reserved + * for I2C bus SCL,SDA !!! + * + * !! WARNING !! + */ + +#ifndef SETTINGS_H +#define SETTINGS_H + +#include + +#ifndef CUSTOM_NETWORK +#define IP_ADDRESS "192.168.1.30" +#define SUBNET "255.255.255.0" +#define GATEWAY "192.168.1.1" +#define DNS1 "192.168.1.4" +#define DNS2 "192.168.1.1" +#define HOSTNAME "arduino-grower" +#else +#define IP_ADDRESS "192.168.1.31" +#define SUBNET "255.255.255.0" +#define GATEWAY "192.168.1.1" +#define DNS1 "192.168.1.4" +#define DNS2 "192.168.1.1" +#define HOSTNAME "test-arduino-grower" +#endif + +#define WEB_SERVER_PORT 80 +#define RADIO_POWER // Enable power control over radio transmission + +#ifdef RADIO_POWER + #define TX_PIN 2 // D4 - orange + #define RCSWITCH_PROTOCOL 13 + /* + * ORNO OR-AE-13132(GS) power extender codes + */ + #define MSG_LENGTH 24 + + #define OUTLET1_ON 0x15533 + #define OUTLET1_OFF 0x1553C + #define OUTLET2_ON 0x155C3 + #define OUTLET2_OFF 0x155CC + #define OUTLET3_ON 0x15703 + #define OUTLET3_OFF 0x1570C + #define OUTLET4_ON 0x15D03 + #define OUTLET4_OFF 0x15D0C + #define OUTLET5_ON 0x17503 + #define OUTLET5_OFF 0x1750C + #define SSR_OUTLET_ON 0x17703 + #define SSR_OUTLET_OFF 0x1770C + + #define HUM_ON_CODE SSR_OUTLET_ON + #define HUM_OFF_CODE SSR_OUTLET_OFF + #define FAN_ON_CODE OUTLET3_ON + #define FAN_OFF_CODE OUTLET3_OFF + #define LAMP_ON_CODE OUTLET4_ON + #define LAMP_OFF_CODE OUTLET4_OFF +#else + #define LAMPRELAYPIN 0 // white/light-brown + #define HUMRELAYPIN 2 // violet + #define FANRELAYPIN 16 // white +#endif + +#define PUMPPIN 14 // D5 - blue +#define LDRPIN A0 // dark-brown/blue + +// TODO: replace const's with define's +/* monitoring constants */ +const uint8_t MAX_TEMP = 40; +const uint8_t TEMP_HYSTERESIS = 10; +// Recommended RH values: +// - vegetative - 60% +// - flowering - 50% +const uint8_t MAX_RH = 70; +const uint8_t RH_HYSTERESIS = 30; +// lamp check interval in seconds +const uint8_t LIGHT_CHECK_INTERVAL = 10; + +#endif \ No newline at end of file diff --git a/include/version.h b/include/version.h index 501caf9..36a377e 100644 --- a/include/version.h +++ b/include/version.h @@ -11,6 +11,6 @@ #ifndef VERSION_H #define VERSION_H -#define VERSION "0.10.0" +#define VERSION "0.12.1" #endif \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 54e7ef2..3ef41d8 100644 --- a/platformio.ini +++ b/platformio.ini @@ -8,7 +8,10 @@ ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html -[env:d1_mini] +[platformio] +default_envs = prod + +[env] platform = espressif8266 board = d1_mini framework = arduino @@ -25,3 +28,10 @@ lib_deps = DHT sensor library for ESPx@1.17 https://github.com/jfturcot/SimpleTimer.git#b30890b8f7046bf3b0258e5870e5dda78ac5c71a https://github.com/enjoyneering/HTU2xD_SHT2x_Si70xx.git#5a76484f6fd0504125851cc23791f0e2a73e6dae + https://github.com/vi7/rc-switch.git#149eba564821e586ef1c221ff8eef1582672bccd + +[env:prod] + +[env:test] +build_flags = + -D CUSTOM_NETWORK diff --git a/src/DHTDevice.cpp b/src/DHTDevice.cpp index fd827f6..3426f06 100644 --- a/src/DHTDevice.cpp +++ b/src/DHTDevice.cpp @@ -21,7 +21,11 @@ void DHTDevice::tempDataHandler(Device* device, uint8_t MAX_TEMP, uint8_t TEMP_H Serial.printf((char*)F("DHT: Failed to read temperature! Device status: %s\n"), dht.getStatusString()); return; } + #ifdef RADIO_POWER + PowerManager::autoPower(&device->isAutoPowerOn, &device->isPowerOn, &temp, MAX_TEMP, TEMP_HYSTERESIS, &device->_onCode, &device->_offCode); + #else PowerManager::autoPower(&device->isAutoPowerOn, &device->isPowerOn, &temp, MAX_TEMP, TEMP_HYSTERESIS, &device->_pin); + #endif } void DHTDevice::rhDataHandler(Device* device, uint8_t MAX_RH, uint8_t RH_HYSTERESIS) { @@ -31,7 +35,11 @@ void DHTDevice::rhDataHandler(Device* device, uint8_t MAX_RH, uint8_t RH_HYSTERE Serial.printf((char*)F("DHT: Failed to read humidity! Device status: %s\n"), dht.getStatusString()); return; } + #ifdef RADIO_POWER + PowerManager::autoPower(&device->isAutoPowerOn, &device->isPowerOn, &rH, MAX_RH, RH_HYSTERESIS, &device->_onCode, &device->_offCode); + #else PowerManager::autoPower(&device->isAutoPowerOn, &device->isPowerOn, &rH, MAX_RH, RH_HYSTERESIS, &device->_pin); + #endif } String DHTDevice::status() { diff --git a/src/DHTDevice.h b/src/DHTDevice.h index 9e2ed4c..d861e01 100644 --- a/src/DHTDevice.h +++ b/src/DHTDevice.h @@ -7,6 +7,7 @@ #include #include +#include "Settings.h" #include "Device.h" #include "MetricsCollectable.h" diff --git a/src/Device.cpp b/src/Device.cpp index 4616f0e..c7a5d34 100644 --- a/src/Device.cpp +++ b/src/Device.cpp @@ -5,12 +5,20 @@ #include "Device.h" void Device::powerOn() { + #ifdef RADIO_POWER + this->isPowerOn = PowerManager::manualPowerOn(&_onCode); + #else this->isPowerOn = PowerManager::manualPowerOn(_pin); + #endif this->isAutoPowerOn = true; } void Device::powerOff() { + #ifdef RADIO_POWER + this->isPowerOn = PowerManager::manualPowerOff(&_offCode); + #else this->isPowerOn = PowerManager::manualPowerOff(_pin); + #endif this->isAutoPowerOn = false; } diff --git a/src/Device.h b/src/Device.h index fb62889..ecbc62f 100644 --- a/src/Device.h +++ b/src/Device.h @@ -6,24 +6,37 @@ #define DEVICE_H #include +#include "Settings.h" #include "PowerManager.h" #include "scheduler.h" class Device { public: - uint8_t _pin; bool isPowerOn; bool isAutoPowerOn; + uint8_t _pin; Device(){}; + #ifdef RADIO_POWER + uint32_t _onCode; + uint32_t _offCode; + + Device(uint32_t onCode, uint32_t offCode): + isAutoPowerOn(true), + _onCode(onCode), + _offCode(offCode) { + this->isPowerOn = PowerManager::manualPowerOn(&onCode); + }; + #else Device(uint8_t pin): _pin(pin), isAutoPowerOn(true) { pinMode(pin, OUTPUT); this->isPowerOn = PowerManager::manualPowerOn(pin); }; + #endif void powerOn(); diff --git a/src/HTU2xDDevice.cpp b/src/HTU2xDDevice.cpp index d6a3924..8e7c346 100644 --- a/src/HTU2xDDevice.cpp +++ b/src/HTU2xDDevice.cpp @@ -18,8 +18,11 @@ void HTU2xDDevice::tempDataHandler(Device* device, uint8_t MAX_TEMP, uint8_t TEM htu2xD->setResolution(HUMD_12BIT_TEMP_14BIT); //humidity 12-bit, temperature 14-bit return; } + #ifdef RADIO_POWER + PowerManager::autoPower(&device->isAutoPowerOn, &device->isPowerOn, &temp, MAX_TEMP, TEMP_HYSTERESIS, &device->_onCode, &device->_offCode); + #else PowerManager::autoPower(&device->isAutoPowerOn, &device->isPowerOn, &temp, MAX_TEMP, TEMP_HYSTERESIS, &device->_pin); - // TODO: add delay(500) as in the HTU2xD_SHT2x_Si70xx lib example? + #endif } void HTU2xDDevice::rhDataHandler(Device* device, uint8_t MAX_RH, uint8_t RH_HYSTERESIS) { @@ -29,7 +32,11 @@ void HTU2xDDevice::rhDataHandler(Device* device, uint8_t MAX_RH, uint8_t RH_HYST Serial.printf((char*)F("%s: Failed to read humidity! CRC8 or communication error occurred\n"), HTU2XD_NAME); return; } + #ifdef RADIO_POWER + PowerManager::autoPower(&device->isAutoPowerOn, &device->isPowerOn, &rH, MAX_RH, RH_HYSTERESIS, &device->_onCode, &device->_offCode); + #else PowerManager::autoPower(&device->isAutoPowerOn, &device->isPowerOn, &rH, MAX_RH, RH_HYSTERESIS, &device->_pin); + #endif } String HTU2xDDevice::status() { diff --git a/src/HTU2xDDevice.h b/src/HTU2xDDevice.h index da6900f..8643fc3 100644 --- a/src/HTU2xDDevice.h +++ b/src/HTU2xDDevice.h @@ -7,6 +7,7 @@ #include #include +#include "Settings.h" #include "Device.h" #include "MetricsCollectable.h" diff --git a/src/PowerManager.cpp b/src/PowerManager.cpp index b4541fb..4754fba 100644 --- a/src/PowerManager.cpp +++ b/src/PowerManager.cpp @@ -4,13 +4,34 @@ #include "PowerManager.h" +#ifdef RADIO_POWER +void PowerManager::autoPower(bool *autoControl, bool *currentState, float *currVal, float maxVal, float valHyst, + uint32_t *onCode, uint32_t *offCode) { + if (!*autoControl) return; + if (*currVal >= maxVal) { + *currentState = manualPowerOff(offCode); + } + else if (*currVal < maxVal - valHyst) { + *currentState = manualPowerOn(onCode); + } +} + +bool PowerManager::manualPowerOn(uint32_t *onCode) { + PowerManager::transmitter.send(*onCode, MSG_LENGTH); + return true; +} +bool PowerManager::manualPowerOff(uint32_t *offCode) { + PowerManager::transmitter.send(*offCode, MSG_LENGTH); + return false; +} +#else void PowerManager::autoPower(bool *autoControl, bool *currentState, float *currVal, float maxVal, float valHyst, uint8_t *pin) { if (!*autoControl) return; - if (*currVal >= maxVal && *currentState) { + if (*currVal >= maxVal) { *currentState = manualPowerOff(*pin); } - else if (*currVal < maxVal - valHyst && !*currentState) { + else if (*currVal < maxVal - valHyst) { *currentState = manualPowerOn(*pin); } } @@ -23,4 +44,5 @@ bool PowerManager::manualPowerOn(uint8_t pin) { bool PowerManager::manualPowerOff(uint8_t pin) { digitalWrite(pin, RELAY_OFF); return false; -} \ No newline at end of file +} +#endif diff --git a/src/PowerManager.h b/src/PowerManager.h index ae3c385..9b5bd3b 100644 --- a/src/PowerManager.h +++ b/src/PowerManager.h @@ -10,12 +10,23 @@ #define RELAY_OFF HIGH #include +#include + +#include "Settings.h" class PowerManager { public: + static RCSwitch transmitter; + #ifdef RADIO_POWER + static void autoPower(bool *autoControl, bool *isOn, float *currVal, float maxVal, float valHyst, + uint32_t *onCode, uint32_t *offCode); + static bool manualPowerOn(uint32_t *onCode); + static bool manualPowerOff(uint32_t *offCode); + #else static void autoPower(bool *autoControl, bool *isOn, float *currVal, float maxVal, float valHyst, uint8_t *pin); static bool manualPowerOn(uint8_t pin); static bool manualPowerOff(uint8_t pin); + #endif }; diff --git a/src/main.cpp b/src/main.cpp index e07974c..bb1dd4a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,15 @@ #include #include #include +#include + +#include "Settings.h" +/* + * include secrets and credentials + * file should be created manually on a basis of "secrets.h.example" + * it'll be ignored by git + */ +#include "secrets.h" #include "scheduler.h" #include "schedules.h" @@ -25,43 +34,19 @@ #include "MetricsExporter.h" #include "MetricsCollectable.h" -/* - * include secrets and credentials - * file should be created manually on a basis of "secrets.h.example" - * it'll be ignored by git - */ -#include "secrets.h" - -/* - * Device pins and wire colors - * - * !!! DO NOT USE pins D1(5),D2(4) - * because they are reserved - * for I2C bus SCL,SDA !!! - */ -#define FANRELAYPIN D0 // white -#define LAMPRELAYPIN D3 // white/light-brown -#define HUMRELAYPIN D4 // violet -#define PUMPPIN D5 // blue -#define LDRPIN A0 // dark-brown/blue - -/* monitoring constants */ -const uint8_t MAX_TEMP = 40; -const uint8_t TEMP_HYSTERESIS = 10; -// Recommended RH values: -// - vegetative - 60% -// - flowering - 50% -const uint8_t MAX_RH = 50; -const uint8_t RH_HYSTERESIS = 15; -// lamp check interval in seconds -const uint8_t LIGHT_CHECK_INTERVAL = 10; - SimpleTimer timer; -WebServer server(80); - -Device fan(FANRELAYPIN); +WebServer server(WEB_SERVER_PORT); + +#ifdef RADIO_POWER +RCSwitch PowerManager::transmitter = RCSwitch(); +Device hum(HUM_ON_CODE, HUM_OFF_CODE); +Device lamp(LAMP_ON_CODE, LAMP_OFF_CODE); +Device fan(FAN_ON_CODE, FAN_OFF_CODE); +#else Device hum(HUMRELAYPIN); Device lamp(LAMPRELAYPIN); +Device fan(FANRELAYPIN); +#endif WaterDevice waterDevice(PUMPPIN); HTU2xDDevice htu2xD; // HTU21D temperature and humidity sensor LDRDevice ldr(LDRPIN); // Light-Dependent Resistor (photoresistor) - light sensor @@ -73,13 +58,30 @@ Scheduler humOnScheduler, humOffScheduler; MetricsExporter htu2xDDeviceExporter((ESP8266WebServer*)&server, (MetricsCollectable*)&htu2xD); - /**************************/ /* FUNCTIONS */ /**************************/ void initWiFi(String SSID, String PSK) { + IPAddress ipAddress; + IPAddress gateway; + IPAddress subnet; + IPAddress dns1; + IPAddress dns2; + + ipAddress.fromString(IP_ADDRESS); + gateway.fromString(GATEWAY); + subnet.fromString(SUBNET); + dns1.fromString(DNS1); + dns2.fromString(DNS2); + + WiFi.disconnect(); + Serial.println(F("Hostname: ") + String(HOSTNAME)); + WiFi.hostname(HOSTNAME); + WiFi.config(ipAddress, gateway, subnet, dns1, dns2); WiFi.begin(SSID, PSK); WiFi.enableSTA(true); + Serial.print(F("MAC: ")); + Serial.println(WiFi.macAddress()); Serial.print("Connecting to: " + SSID); while (WiFi.status() != WL_CONNECTED) @@ -87,14 +89,19 @@ void initWiFi(String SSID, String PSK) { delay(500); Serial.print("."); } + Serial.println(); Serial.println(F("WiFi connected")); - Serial.print(F("MAC: ")); - Serial.println(WiFi.macAddress()); Serial.print(F("IP address: ")); Serial.println(WiFi.localIP()); } +void initRadio() { + PowerManager::transmitter.enableTransmit(TX_PIN); + PowerManager::transmitter.setProtocol(RCSWITCH_PROTOCOL); + Serial.println(F("Radio transmission initialized")); +} + void systemRestart() { Serial.println(F("Restarting device...")); ESP.restart(); @@ -112,9 +119,12 @@ void setup() { Serial.printf("%s", BOARD_IDENTITY); Serial.println(F(" is up. Hey there!")); Serial.printf("Firmware version: %s\n", VERSION); - Serial.println(F("************\n\n")); + Serial.println(F("************\n")); initWiFi(WIFI_SSID, WIFI_PSK); + #ifdef RADIO_POWER + initRadio(); + #endif /* SimpleTimer function execution scheduling */ timer.setInterval(htu2xD.getReadInterval(), []{htu2xD.tempDataHandler(&lamp, MAX_TEMP, TEMP_HYSTERESIS);}); @@ -135,9 +145,9 @@ void setup() { lampOnScheduler = Scheduler([]{lamp.scheduledPowerOn(lampOnScheduler);}, LAMP_ON_SCHEDULE); lampOffScheduler = Scheduler([]{lamp.scheduledPowerOff(lampOffScheduler);}, LAMP_OFF_SCHEDULE); /* - * Fan on/off schedule disabled due to switch on issues + * Fan on/off schedule disabled due to power on issues * while using computer fan with AC/DC 230V/12V power supply - * This is probably caused by the usage of solid-state relays + * It is probably caused by the usage of solid-state relays * See https://omronfs.omron.com/en_US/ecb/products/pdf/precautions_ssr.pdf */ // fanOnScheduler = Scheduler([]{fan.scheduledPowerOn(fanOnScheduler);}, FAN_ON_SCHEDULE);