├── Dialog_Plain_18_font.h ├── Extra_Utilities.ino ├── P127_LibTeleinfo_Library ├── LibTeleinfo.cpp ├── LibTeleinfo.h └── Readme.md ├── P178_Frogmore_SCD30_Arduino_Library ├── FrogmoreScd30.cpp └── FrogmoreScd30.h ├── PluginStructs ├── P248_data_struct.cpp └── P248_data_struct.h ├── README.md ├── _C004.ino ├── _C022.ino ├── _C025.ino ├── _P096_Vito.ino ├── _P100_SRF01.ino ├── _P101_NeoClock.ino ├── _P102_Nodo.ino ├── _P103_Event.ino ├── _P104_SRF02.ino ├── _P105_RGBW.ino ├── _P106_IRTX.ino ├── _P107_Email_Demo.ino ├── _P108_WOL.ino ├── _P109_RESOL_DeltaSol_Pro.ino ├── _P110_P1WifiGateway.ino ├── _P111_RF.ino ├── _P111_SenseAir.ino ├── _P112_Power.ino ├── _P112_RFTX.ino ├── _P113_SI1145.ino ├── _P114_DSM501.ino ├── _P115_HeatpumpIR.ino ├── _P116_ID12.ino ├── _P117_LW12FC.ino ├── _P117_Neopixels ├── _P117_Nextion.ino ├── _P118_CCS811.ino ├── _P119_BME680.ino ├── _P120_Thermocouple.ino ├── _P121_Candle.ino ├── _P122_NeoPixel.ino ├── _P123_SI7013.ino ├── _P124_NeoPixelBusFX.ino ├── _P125_ArduCAM.ino ├── _P126_HassDiscovery.ino ├── _P126_Ping.ino ├── _P127_Teleinfo.ino ├── _P128_AS3935.ino ├── _P128_HassSwitch.ino ├── _P129_RC522_RFID.ino ├── _P130_VEML6075.ino ├── _P131_SHT3X.ino ├── _P133_VL53L0X.ino ├── _P134_PPD42.ino ├── _P135_MQ135.ino ├── _P136_TMP117.ino ├── _P141_LedStrip.ino ├── _P142_RGB-Strip.ino ├── _P143_AnyonePresent.ino ├── _P144_RC-Switch-TX.ino ├── _P145_Itho.ino ├── _P149_MHZ19.ino ├── _P150_SDM120C.ino ├── _P151_CISA.ino ├── _P152_MCP42010.ino ├── _P153_MAX44009.ino ├── _P156_DS1307.ino ├── _P157_DS3231.ino ├── _P158_Yeti.ino ├── _P159_Pushbutton.ino ├── _P160_OutputMulti.ino ├── _P161_Switchboard.ino ├── _P162_MPL3115A2.ino ├── _P163_DS1631.ino ├── _P165_SerSwitch.ino ├── _P166_WiFiMan.ino ├── _P167_ADS1015.ino ├── _P168_ThermOLED.ino ├── _P169_FO_WH24_RFM69.ino ├── _P170_HLW8012.ino ├── _P171_PZEM-004T.ino ├── _P176_CDM7160.ino ├── _P178_SCD30.ino ├── _P180_Mux.ino ├── _P181_TempHumidity_SHT2x.ino ├── _P182_MT681.ino ├── _P183_SMA.ino ├── _P184_GY_US42V2.ino ├── _P190_Ventus_W266_RFM69.ino ├── _P197_RFLink_MQTT_Bridge.ino ├── _P198_Venta.ino ├── _P199_RF443_KaKu.ino ├── _P201_VBAN.ino ├── _P202_ADC_ACcurrentSensor.ino ├── _P203_Feeder.ino ├── _P205_FrameOLED.ino ├── _P208_Nokia_LCD_5110.ino ├── _P209_IFTTTMaker.ino ├── _P210_MQTTImport.ino ├── _P211_MPU6050.ino ├── _P212_MY9291.ino ├── _P213_VEML6070 ├── _P216_LIFX.ino ├── _P216_PMS5003ST.ino ├── _P216_PMSx003ST.ino ├── _P217_ACS712AC.ino ├── _P218_BlindControl.ino ├── _P218_MaxbotixEZ_Distance.ino ├── _P218_SlowPWM.ino ├── _P221_LandisGyrUH550.ino ├── _P222_DebouncedCounter.ino ├── _P224_DDS238.ino ├── _P242_ILI9341.ino ├── _P243_Schedule.ino ├── _P245_Hoben.ino ├── _P246_ADCfast.ino ├── _P246_DAC.ino ├── _P247_DallasCounter.ino ├── _P248_AHT.ino ├── _P248_HomEvap.ino ├── _P248_TempHumidity_AHT1x.ino ├── _P249_GoodWeModbus.ino ├── esp8266-oled-ssd1306.zip ├── libraries _PLUGIN145 ITHO FAN └── Itho │ ├── CC1101.cpp │ ├── CC1101.h │ ├── CC1101Packet.h │ ├── IthoCC1101.cpp │ ├── IthoCC1101.h │ └── IthoPacket.h └── src └── PluginStructs ├── P248_data_struct.cpp └── P248_data_struct.h /P127_LibTeleinfo_Library/Readme.md: -------------------------------------------------------------------------------- 1 | Copy of https://github.com/marco402/LibTeleinfo/tree/rewrite-Wifinfo/examples/Wifinfo 2 | ( with "mySyslog.h" and "Wifinfo.h" commented in LibTeleinfo.h ) 3 | 4 | This version has been choosen because it is compliant with the STANDARD version of the teleinfo protocol. 5 | The original hallard's lib was heavily forked but suffer from feedback and PR 6 | 7 | -------------------------------------------------------------------------------- /P178_Frogmore_SCD30_Arduino_Library/FrogmoreScd30.h: -------------------------------------------------------------------------------- 1 | /* 2 | # Copyright (c) 2019 Frogmore42 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | */ 21 | #pragma once 22 | 23 | #include "Arduino.h" 24 | 25 | //#define SCD30_DEBUG 26 | 27 | #define SCD30_ADDRESS 0x61 28 | #define ERROR_SCD30_NO_ERROR 0 29 | #define ERROR_SCD30_NO_DATA 0x80000000 30 | #define ERROR_SCD30_CO2_ZERO 0x90000000 31 | #define ERROR_SCD30_UNKNOWN_ERROR 0x1000000 32 | #define ERROR_SCD30_CRC_ERROR 0x2000000 33 | #define ERROR_SCD30_NOT_ENOUGH_BYTES_ERROR 0x3000000 34 | #define ERROR_SCD30_NOT_FOUND_ERROR 0x4000000 35 | #define ERROR_SCD30_NOT_A_NUMBER_ERROR 0x5000000 36 | #define ERROR_SCD30_INVALID_VALUE 0x6000000 37 | 38 | #define SCD30_MEDIAN_FILTER_SIZE 5 39 | 40 | class FrogmoreScd30 41 | { 42 | public: 43 | FrogmoreScd30() {}; 44 | // Constructors 45 | // the SCD30 only lists a single i2c address, so not necesary to specify 46 | // 47 | void begin(void); 48 | void begin(uint8_t _i2cAddress); 49 | void begin(TwoWire *pWire); 50 | void begin(TwoWire *pWire, uint8_t _i2cAddress); 51 | 52 | int softReset(void); 53 | int clearI2CBus(void); // this is a HARD reset of the IC2 bus to restore communication, it will disrupt the bus 54 | 55 | int getAltitudeCompensation(uint16_t *pHeight_meter); 56 | int getAmbientPressure(uint16_t *pAirPressure_mbar); 57 | int getCalibrationType(uint16_t *pIsAuto); 58 | int getFirmwareVersion(uint8_t *pMajor, uint8_t *pMinor); 59 | int getForcedRecalibrationFactor(uint16_t *pCo2_ppm); 60 | int getMeasurementInterval(uint16_t *pTime_sec); 61 | int getTemperatureOffset(float *pOffset_degC); 62 | int getTemperatureOffset(uint16_t *pOffset_centiDegC); 63 | 64 | int setAltitudeCompensation(uint16_t height_meter); 65 | int setAmbientPressure(uint16_t airPressure_mbar); 66 | int setAutoSelfCalibration(void); 67 | int setCalibrationType(bool isAuto); 68 | int setForcedRecalibrationFactor(uint16_t co2_ppm); 69 | int setManualCalibration(void); 70 | int setMeasurementInterval(uint16_t time_sec); 71 | int setTemperatureOffset(float offset_degC); 72 | int setTemperatureOffset(uint16_t offset_centiDegC); 73 | 74 | int beginMeasuring(void); 75 | int beginMeasuring(uint16_t airPressure_mbar); // also sets ambient pressure offset in mbar/hPascal 76 | int isDataAvailable(bool *pIsAvailable); 77 | int readMeasurement( 78 | uint16 *pCO2_ppm, 79 | uint16 *pCO2EAvg_ppm, 80 | float *pTemperature, 81 | float *pHumidity 82 | ); 83 | int stopMeasuring(void); 84 | 85 | private: 86 | uint8_t i2cAddress; 87 | TwoWire *pWire; 88 | uint16_t ambientPressure; 89 | uint16_t co2AvgExtra; 90 | uint16_t co2History[SCD30_MEDIAN_FILTER_SIZE]; 91 | uint16_t co2EAverage; 92 | int8_t co2NewDataLocation; // location to put new CO2 data for median filter 93 | 94 | uint8_t computeCRC8(uint8_t data[], uint8_t len); 95 | int sendBytes(void *pInput, uint8_t len); 96 | int getBytes(void *pOutput, uint8_t len); 97 | int sendCommand(uint16_t command); 98 | int sendCommandArguments(uint16_t command, uint16_t arguments); 99 | int get16BitRegCheckCRC(void* pInput, uint16_t* pData); 100 | int get32BitRegCheckCRC(void* pInput, float* pData); 101 | int readRegister(uint16_t registerAddress, uint16_t* pData); 102 | #ifdef SCD30_DEBUG 103 | void AddLog(uint8_t loglevel); 104 | #endif 105 | }; 106 | -------------------------------------------------------------------------------- /PluginStructs/P248_data_struct.cpp: -------------------------------------------------------------------------------- 1 | #ifdef USES_P248 2 | 3 | #include "../PluginStructs/P248_data_struct.h" 4 | 5 | 6 | uint8_t AHTSetCalCmd[3] = { AHTX_CMD, 0x08, 0x00 }; // load factory calibration coeff 7 | uint8_t AHTSetCycleCmd[3] = { AHTX_CMD, 0x28, 0x00 }; // enable cycle mode 8 | uint8_t AHTMeasureCmd[3] = { 0xAC, 0x33, 0x00 }; // start measurement command 9 | uint8_t AHTResetCmd = 0xBA; // soft reset command 10 | 11 | 12 | P248_data_struct::P248_data_struct(uint8_t addr) : 13 | last_hum_val(0.0f), 14 | last_temp_val(0.0f), 15 | last_measurement(0), 16 | i2cAddress(addr), 17 | state(AHT_state::Uninitialized) {} 18 | 19 | 20 | bool P248_data_struct::initialized() const { 21 | return state != Uninitialized; 22 | } 23 | 24 | // Only perform the measurements with big interval to prevent the sensor from warming up. 25 | bool P248_data_struct::update(unsigned long task_index) { 26 | const unsigned long current_time = millis(); 27 | 28 | if (!initialized()) { 29 | if (!begin()) { 30 | return false; 31 | } 32 | state = Initialized; 33 | last_measurement = 0; 34 | } 35 | 36 | if (state != Wait_for_samples) { 37 | 38 | last_measurement = current_time; 39 | 40 | startMeasurement(); 41 | state = Wait_for_samples; 42 | return false; 43 | } 44 | 45 | //make sure we wait for the measurement to complete 46 | if (!timeOutReached(last_measurement + AHT10_MEASURMENT_DELAY)) { 47 | return false; 48 | } 49 | 50 | if (!readMeasurement()) { 51 | state = Initialized; 52 | return false; 53 | } 54 | 55 | last_measurement = current_time; 56 | state = New_values; 57 | last_temp_val = readTemperature(); 58 | last_hum_val = readHumidity(); 59 | 60 | 61 | /* 62 | String log; 63 | 64 | if (loglevelActiveFor(LOG_LEVEL_INFO)) { 65 | log.reserve(120); // Prevent re-allocation 66 | log = getDeviceName(); 67 | log += F(":"); 68 | } 69 | boolean logAdded = false; 70 | 71 | 72 | if ((tempOffset > 0.1f) || (tempOffset < -0.1f)) { 73 | // There is some offset to apply. 74 | if (loglevelActiveFor(LOG_LEVEL_INFO)) { 75 | log += F(" Apply temp offset "); 76 | log += tempOffset; 77 | log += F("C"); 78 | } 79 | 80 | if (hasHumidity()) { 81 | if (loglevelActiveFor(LOG_LEVEL_INFO)) { 82 | log += F(" humidity "); 83 | log += last_hum_val; 84 | } 85 | last_hum_val = compute_humidity_from_dewpoint(last_temp_val + tempOffset, last_dew_temp_val); 86 | 87 | if (loglevelActiveFor(LOG_LEVEL_INFO)) { 88 | log += F("% => "); 89 | log += last_hum_val; 90 | log += F("%"); 91 | } 92 | } else { 93 | last_hum_val = 0.0f; 94 | } 95 | 96 | if (loglevelActiveFor(LOG_LEVEL_INFO)) { 97 | log += F(" temperature "); 98 | log += last_temp_val; 99 | } 100 | last_temp_val = last_temp_val + tempOffset; 101 | 102 | if (loglevelActiveFor(LOG_LEVEL_INFO)) { 103 | log += F("C => "); 104 | log += last_temp_val; 105 | log += F("C"); 106 | logAdded = true; 107 | } 108 | } 109 | 110 | if (hasHumidity()) { 111 | if (loglevelActiveFor(LOG_LEVEL_INFO)) { 112 | log += F(" dew point "); 113 | log += last_dew_temp_val; 114 | log += F("C"); 115 | logAdded = true; 116 | } 117 | } 118 | 119 | if (logAdded && loglevelActiveFor(LOG_LEVEL_INFO)) { 120 | addLog(LOG_LEVEL_INFO, log); 121 | } 122 | 123 | */ 124 | 125 | return true; 126 | } 127 | 128 | 129 | bool P248_data_struct::begin() { 130 | Wire.beginTransmission(i2cAddress); 131 | Wire.write(AHTResetCmd); 132 | Wire.endTransmission(); 133 | 134 | delay(AHT10_SOFT_RESET_DELAY); 135 | 136 | Wire.beginTransmission(i2cAddress); 137 | Wire.write(AHTSetCalCmd, 3); 138 | if (Wire.endTransmission() != 0) 139 | return false; 140 | 141 | delay(AHT10_MEASURMENT_DELAY); 142 | 143 | if(ReadStatus(i2cAddress) & 0x08) // Sensor calibrated? 144 | return true; 145 | 146 | return false; 147 | } 148 | 149 | /**************************************************************************/ 150 | /* 151 | readStatusByte() 152 | Read status byte from sensor over I2C 153 | */ 154 | /**************************************************************************/ 155 | unsigned inline char P248_data_struct::ReadStatus(uint8_t address) { 156 | //uint8_t result = 0; 157 | // Need for AHT20? 158 | //Wire.beginTransmission(aht1x_address); 159 | //Wire.write(0x71); 160 | //if (Wire.endTransmission() != 0) return false; 161 | Wire.requestFrom(address, (uint8_t) 1); 162 | return Wire.read(); 163 | } 164 | 165 | 166 | 167 | 168 | /**************************************************************************/ 169 | /* 170 | readTemperature() 171 | Read temperature, °C 172 | NOTE: 173 | - temperature range -40°C..+80°C 174 | - temperature resolution 0.01°C 175 | - temperature accuracy ±0.3°C 176 | */ 177 | /**************************************************************************/ 178 | float P248_data_struct::readTemperature() 179 | { 180 | if (AHT10_rawDataBuffer[0] == AHT10_ERROR) return AHT10_ERROR; //error handler, collision on I2C bus 181 | 182 | uint32_t temperature = ((uint32_t)(AHT10_rawDataBuffer[3] & 0x0F) << 16) | ((uint32_t)AHT10_rawDataBuffer[4] << 8) | AHT10_rawDataBuffer[5]; //20-bit raw temperature data 183 | 184 | return static_cast(temperature) * 0.000191f - 50.0f; 185 | } 186 | 187 | 188 | /**************************************************************************/ 189 | /* 190 | readHumidity() 191 | Read relative humidity, % 192 | NOTE: 193 | - prolonged exposure for 60 hours at humidity > 80% can lead to a 194 | temporary drift of the signal +3%. Sensor slowly returns to the 195 | calibrated state at normal operating conditions. 196 | - relative humidity range 0%..100% 197 | - relative humidity resolution 0.024% 198 | - relative humidity accuracy ±2% 199 | */ 200 | /**************************************************************************/ 201 | float P248_data_struct::readHumidity() 202 | { 203 | if (AHT10_rawDataBuffer[0] == AHT10_ERROR) return AHT10_ERROR; //error handler, collision on I2C bus 204 | 205 | uint32_t rawData = (((uint32_t)AHT10_rawDataBuffer[1] << 16) | ((uint32_t)AHT10_rawDataBuffer[2] << 8) | (AHT10_rawDataBuffer[3])) >> 4; //20-bit raw humidity data 206 | 207 | const float humidity = static_cast(rawData) * 0.000095f; 208 | 209 | if (humidity < 0) return 0; 210 | if (humidity > 100) return 100; 211 | 212 | return humidity; 213 | } 214 | 215 | 216 | bool P248_data_struct::startMeasurement() { 217 | Wire.beginTransmission(i2cAddress); 218 | Wire.write(AHTMeasureCmd, 3); 219 | if (Wire.endTransmission() != 0) 220 | return false; 221 | return true; 222 | } 223 | 224 | bool P248_data_struct::readMeasurement() { 225 | Wire.requestFrom(i2cAddress, (uint8_t) 6); 226 | 227 | for(uint8_t i = 0; Wire.available() > 0 && i < 6; i++) { 228 | AHT10_rawDataBuffer[i] = Wire.read(); 229 | } 230 | 231 | if (AHT10_rawDataBuffer[0] & 0x80) 232 | return false; //device is busy 233 | 234 | return true; 235 | } 236 | 237 | #endif // ifdef USES_P248 238 | -------------------------------------------------------------------------------- /PluginStructs/P248_data_struct.h: -------------------------------------------------------------------------------- 1 | #ifndef PLUGINSTRUCTS_P248_DATA_STRUCT_H 2 | #define PLUGINSTRUCTS_P248_DATA_STRUCT_H 3 | 4 | #ifdef USES_P248 5 | 6 | #include "../../_Plugin_Helper.h" 7 | 8 | // https://wiki.liutyi.info/display/ARDUINO/AHT10 9 | // https://github.com/enjoyneering/AHT10/blob/master/src/AHT10.h 10 | // https://github.com/adafruit/Adafruit_AHTX0 11 | // https://github.com/arendst/Tasmota/blob/0650744ac27108931a070918f08173d71ddfd68d/tasmota/xsns_63_aht1x.ino 12 | 13 | #define AHT10_ADDRESS_0X38 0x38 //chip I2C address no.1 for AHT10/AHT15/AHT20, address pin connected to GND 14 | #define AHT10_ADDRESS_0X39 0x39 //chip I2C address no.2 for AHT10 only, address pin connected to Vcc 15 | 16 | #define AHT10_INIT_CMD 0xE1 //initialization command for AHT10/AHT15 17 | #define AHT20_INIT_CMD 0xBE //initialization command for AHT20 18 | #define AHT10_START_MEASURMENT_CMD 0xAC //start measurment command 19 | #define AHT10_NORMAL_CMD 0xA8 //normal cycle mode command, no info in datasheet!!! 20 | #define AHT10_SOFT_RESET_CMD 0xBA //soft reset command 21 | 22 | #define AHT10_INIT_NORMAL_MODE 0x00 //enable normal mode 23 | #define AHT10_INIT_CYCLE_MODE 0x20 //enable cycle mode 24 | #define AHT10_INIT_CMD_MODE 0x40 //enable command mode 25 | #define AHT10_INIT_CAL_ENABLE 0x08 //load factory calibration coeff 26 | 27 | 28 | #define AHT10_DATA_MEASURMENT_CMD 0x33 //no info in datasheet!!! my guess it is DAC resolution, saw someone send 0x00 instead 29 | #define AHT10_DATA_NOP 0x00 //no info in datasheet!!! 30 | 31 | 32 | #define AHT10_MEASURMENT_DELAY 80 //at least 75 milliseconds 33 | #define AHT10_POWER_ON_DELAY 40 //at least 20..40 milliseconds 34 | #define AHT10_CMD_DELAY 350 //at least 300 milliseconds, no info in datasheet!!! 35 | #define AHT10_SOFT_RESET_DELAY 20 //less than 20 milliseconds 36 | 37 | #define AHT10_FORCE_READ_DATA true //force to read data 38 | #define AHT10_USE_READ_DATA false //force to use data from previous read 39 | #define AHT10_ERROR 0xFF //returns 255, if communication error is occurred 40 | 41 | 42 | #define AHTX0_I2CADDR_DEFAULT 0x38 ///< AHT default i2c address 43 | #define AHTX0_CMD_CALIBRATE 0xE1 ///< Calibration command 44 | #define AHTX0_CMD_TRIGGER 0xAC ///< Trigger reading command 45 | #define AHTX0_CMD_SOFTRESET 0xBA ///< Soft reset command 46 | #define AHTX0_STATUS_BUSY 0x80 ///< Status bit for busy 47 | #define AHTX0_STATUS_CALIBRATED 0x08 ///< Status bit for calibrated 48 | 49 | 50 | #ifdef USE_AHT2x 51 | #define AHTX_CMD 0xB1 // Cmd for AHT2x 52 | #else 53 | #define AHTX_CMD 0xE1 // Cmd for AHT1x 54 | #endif 55 | 56 | 57 | 58 | 59 | 60 | 61 | enum AHT_state { 62 | Uninitialized = 0, 63 | Initialized, 64 | Wait_for_samples, 65 | New_values, 66 | Values_read 67 | }; 68 | 69 | 70 | struct P248_data_struct : public PluginTaskData_base { 71 | P248_data_struct(uint8_t addr); 72 | 73 | bool initialized() const; 74 | 75 | bool begin(); 76 | 77 | // Only perform the measurements with big interval to prevent the sensor from warming up. 78 | bool update(unsigned long task_index); 79 | 80 | bool startMeasurement(); 81 | bool readMeasurement(); 82 | unsigned inline char ReadStatus(uint8_t address); 83 | 84 | // **************************************************************************/ 85 | // Read temperature 86 | // **************************************************************************/ 87 | float readTemperature(); 88 | 89 | // **************************************************************************/ 90 | // Read humidity 91 | // **************************************************************************/ 92 | float readHumidity(); 93 | 94 | 95 | uint8_t AHT10_rawDataBuffer[6] = {AHT10_ERROR, 0, 0, 0, 0, 0}; 96 | float last_hum_val = 0.0f; 97 | float last_temp_val = 0.0f; 98 | unsigned long last_measurement = 0; 99 | uint8_t i2cAddress = 0; 100 | AHT_state state = AHT_state::Uninitialized; 101 | }; 102 | 103 | #endif // ifdef USES_P248 104 | 105 | #endif // PLUGINSTRUCTS_P248_DATA_STRUCT_H 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESPEasyPluginPlayground 2 | Plugin ideas, concepts, user contributed, etc 3 | 4 | Plugin ID's should be unique and numbered between 100 - 199. Filenames should use the \_Pxxx\_ prefix. 5 | 6 | Remember: Playground plugins can be in any state from development to stable and they are published without verification by the development team. 7 | 8 | The main ESPEasy repo and plugins are here: https://github.com/letscontrolit/ESPEasy 9 | 10 | ## Getting the plugin into the main repository 11 | 12 | Look here for the guidelines on getting the plugin in the main-repository, and make it a standard part of ESPEasy: https://www.letscontrolit.com/wiki/index.php/ESPEasyDevelopmentGuidelines 13 | -------------------------------------------------------------------------------- /_C004.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //########################### Controller Plugin 104: ThingSpeak ######################################### 3 | //####################################################################################################### 4 | 5 | #define CPLUGIN_004 6 | #define CPLUGIN_ID_004 4 7 | #define CPLUGIN_NAME_004 "ThingSpeak" 8 | 9 | #define NUM_OF_FIELDS 8 10 | #define THINGSPEAK_DELAY 15 11 | 12 | float field_data[NUM_OF_FIELDS]; 13 | byte field_format[NUM_OF_FIELDS]; 14 | unsigned long thingspeak_timer; 15 | 16 | boolean CPlugin_004(byte function, struct EventStruct *event, String& string) 17 | { 18 | boolean success = false; 19 | byte x; 20 | 21 | switch (function) 22 | { 23 | case CPLUGIN_PROTOCOL_ADD: 24 | { 25 | Protocol[++protocolCount].Number = CPLUGIN_ID_004; 26 | Protocol[protocolCount].usesMQTT = false; 27 | Protocol[protocolCount].usesAccount = false; 28 | Protocol[protocolCount].usesPassword = true; 29 | Protocol[protocolCount].defaultPort = 80; 30 | 31 | thingspeak_timer = millis() + THINGSPEAK_DELAY*1000 + 30000; 32 | for(byte i=0; isensorType); 52 | for (x = 0; x < valueCount; x++) 53 | { 54 | if(event->idx + x - 1 < NUM_OF_FIELDS) 55 | { 56 | field_data[event->idx + x - 1] = UserVar[event->BaseVarIndex + x]; 57 | field_format[event->idx + x - 1] = ExtraTaskSettings.TaskDeviceValueDecimals[x]; 58 | } 59 | } 60 | 61 | 62 | if(millis() > thingspeak_timer) 63 | { 64 | String postDataStr = F("api_key="); 65 | postDataStr += SecuritySettings.ControllerPassword; // used for API key 66 | boolean sendUpdate = false; 67 | for (x = 0; x < NUM_OF_FIELDS; x++) 68 | { 69 | if(field_format[x]!=255) 70 | { 71 | postDataStr += F("&field"); 72 | postDataStr += x+1; 73 | postDataStr += "="; 74 | int tmp = (int)(field_data[x]*100); 75 | postDataStr += toString(field_data[x],field_format[x]); 76 | sendUpdate = true; 77 | } 78 | } 79 | 80 | if(sendUpdate) 81 | { 82 | thingspeak_timer = millis() + THINGSPEAK_DELAY*1000; 83 | 84 | char host[20]; 85 | sprintf_P(host, PSTR("%u.%u.%u.%u"), Settings.Controller_IP[0], Settings.Controller_IP[1], Settings.Controller_IP[2], Settings.Controller_IP[3]); 86 | 87 | sprintf_P(log, PSTR("%s%s using port %u"), "HTTP : connecting to ", host,Settings.ControllerPort); 88 | addLog(LOG_LEVEL_DEBUG, log); 89 | 90 | WiFiClient client; 91 | if (!client.connect(host, Settings.ControllerPort)) 92 | { 93 | connectionFailures++; 94 | strcpy_P(log, PSTR("HTTP : connection failed")); 95 | addLog(LOG_LEVEL_ERROR, log); 96 | thingspeak_timer -= Settings.Delay*1000; 97 | return false; 98 | } 99 | statusLED(true); 100 | if (connectionFailures) 101 | connectionFailures--; 102 | 103 | String hostName = F("api.thingspeak.com"); // PM_CZ: HTTP requests must contain host headers. 104 | if (Settings.UseDNS) 105 | hostName = Settings.ControllerHostName; 106 | 107 | String postStr = F("POST /update HTTP/1.1\r\n"); 108 | postStr += F("Host: "); 109 | postStr += hostName; 110 | postStr += F("\r\n"); 111 | postStr += F("Connection: close\r\n"); 112 | 113 | postStr += F("Content-Type: application/x-www-form-urlencoded\r\n"); 114 | postStr += F("Content-Length: "); 115 | postStr += postDataStr.length(); 116 | postStr += F("\r\n\r\n"); 117 | postStr += postDataStr; 118 | 119 | // This will send the request to the server 120 | client.print(postStr); 121 | 122 | unsigned long timer = millis() + 200; 123 | while (!client.available() && millis() < timer) 124 | delay(1); 125 | 126 | // Read all the lines of the reply from server and print them to Serial 127 | while (client.available()) { 128 | String line = client.readStringUntil('\n'); 129 | line.toCharArray(log, 80); 130 | addLog(LOG_LEVEL_DEBUG_MORE, log); 131 | if (line.substring(0, 15) == "HTTP/1.1 200 OK") 132 | { 133 | strcpy_P(log, PSTR("HTTP : Succes!")); 134 | addLog(LOG_LEVEL_DEBUG, log); 135 | success = true; 136 | } 137 | delay(1); 138 | } 139 | strcpy_P(log, PSTR("HTTP : closing connection")); 140 | addLog(LOG_LEVEL_DEBUG, log); 141 | 142 | client.flush(); 143 | client.stop(); 144 | 145 | for(byte i=0; i210" core statement to comply (and function) with new standard 230 core as of R114, 24 September 2016 13 | * 14 | /******************************************************************************/ 15 | 16 | #define CPLUGIN_022 17 | #define CPLUGIN_ID_022 22 18 | #define CPLUGIN_NAME_022 "Pimatic RestApi" 19 | 20 | boolean CPlugin_022(byte function, struct EventStruct *event, String& string) 21 | { 22 | boolean success = false; 23 | 24 | switch (function) 25 | { 26 | case CPLUGIN_PROTOCOL_ADD: 27 | { 28 | Protocol[++protocolCount].Number = CPLUGIN_ID_022; 29 | Protocol[protocolCount].usesMQTT = false; 30 | Protocol[protocolCount].usesAccount = true; 31 | Protocol[protocolCount].usesPassword = true; 32 | Protocol[protocolCount].defaultPort = 80; 33 | break; 34 | } 35 | 36 | case CPLUGIN_GET_DEVICENAME: 37 | { 38 | string = F(CPLUGIN_NAME_022); 39 | break; 40 | } 41 | 42 | case CPLUGIN_PROTOCOL_SEND: 43 | { 44 | switch (event->sensorType) 45 | { 46 | case SENSOR_TYPE_SINGLE: // single value sensor, used for Dallas, BH1750, etc 47 | case SENSOR_TYPE_SWITCH: 48 | case SENSOR_TYPE_DIMMER: 49 | pimaticUpdateVariable(event, 0, UserVar[event->BaseVarIndex], 0); 50 | break; 51 | case SENSOR_TYPE_LONG: // single LONG value, stored in two floats (rfid tags) 52 | pimaticUpdateVariable(event, 0, 0, (unsigned long)UserVar[event->BaseVarIndex] + ((unsigned long)UserVar[event->BaseVarIndex + 1] << 16)); 53 | break; 54 | case SENSOR_TYPE_TEMP_HUM: 55 | case SENSOR_TYPE_TEMP_BARO: 56 | { 57 | pimaticUpdateVariable(event, 0, UserVar[event->BaseVarIndex], 0); 58 | unsigned long timer = millis() + Settings.MessageDelay; 59 | while (millis() < timer) 60 | backgroundtasks(); 61 | pimaticUpdateVariable(event, 1, UserVar[event->BaseVarIndex + 1], 0); 62 | break; 63 | } 64 | case SENSOR_TYPE_TEMP_HUM_BARO: 65 | { 66 | pimaticUpdateVariable(event, 0, UserVar[event->BaseVarIndex], 0); 67 | unsigned long timer = millis() + Settings.MessageDelay; 68 | while (millis() < timer) 69 | backgroundtasks(); 70 | pimaticUpdateVariable(event, 1, UserVar[event->BaseVarIndex + 1], 0); 71 | timer = millis() + Settings.MessageDelay; 72 | while (millis() < timer) 73 | backgroundtasks(); 74 | pimaticUpdateVariable(event, 2, UserVar[event->BaseVarIndex + 2], 0); 75 | break; 76 | } 77 | } 78 | break; 79 | } 80 | 81 | } 82 | return success; 83 | } 84 | 85 | 86 | //******************************************************************************** 87 | // Pimatic updateVariable 88 | //******************************************************************************** 89 | boolean pimaticUpdateVariable(struct EventStruct *event, byte varIndex, float value, unsigned long longValue) 90 | { 91 | 92 | String authHeader = ""; 93 | 94 | if ((SecuritySettings.ControllerUser[0] != 0) && (SecuritySettings.ControllerPassword[0] != 0)) 95 | { 96 | base64 encoder; 97 | String auth = SecuritySettings.ControllerUser; 98 | auth += ":"; 99 | auth += SecuritySettings.ControllerPassword; 100 | authHeader = "Authorization: Basic " + encoder.encode(auth) + " \r\n"; 101 | } 102 | 103 | 104 | char log[80]; 105 | boolean success = false; 106 | char host[20]; 107 | sprintf_P(host, PSTR("%u.%u.%u.%u"), Settings.Controller_IP[0], Settings.Controller_IP[1], Settings.Controller_IP[2], Settings.Controller_IP[3]); 108 | 109 | sprintf_P(log, PSTR("%s%s using port %u"), "HTTP : connecting to ", host,Settings.ControllerPort); 110 | addLog(LOG_LEVEL_DEBUG, log); 111 | 112 | // Use WiFiClient class to create TCP connections 113 | WiFiClient client; 114 | if (!client.connect(host, Settings.ControllerPort)) 115 | { 116 | connectionFailures++; 117 | strcpy_P(log, PSTR("HTTP : connection failed")); 118 | addLog(LOG_LEVEL_ERROR, log); 119 | return false; 120 | } 121 | statusLED(true); 122 | if (connectionFailures) 123 | connectionFailures--; 124 | 125 | if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0) 126 | PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString); 127 | 128 | String url = "/api/variables/"; 129 | url += URLEncode(ExtraTaskSettings.TaskDeviceValueNames[varIndex]); 130 | url.toCharArray(log, 80); 131 | addLog(LOG_LEVEL_DEBUG_MORE, log); 132 | 133 | String data; 134 | if (longValue) 135 | data = String(longValue); 136 | else 137 | data = toString(value, ExtraTaskSettings.TaskDeviceValueDecimals[varIndex]); 138 | 139 | String yourdata = "{\"type\": \"value\", \"valueOrExpression\": \"" + data + "\"}"; 140 | 141 | String hostName = host; 142 | if (Settings.UseDNS) 143 | hostName = Settings.ControllerHostName; 144 | 145 | // This will send the request to the server 146 | client.print(String("PATCH ") + url + " HTTP/1.1\r\n" + 147 | authHeader + 148 | "Host: " + hostName + "\r\n" + 149 | "Content-Type:application/json\r\n" + 150 | "Content-Length: " + yourdata.length() + "\r\n\r\n" + 151 | yourdata); 152 | 153 | unsigned long timer = millis() + 200; 154 | while (!client.available() && millis() < timer) 155 | delay(1); 156 | 157 | // Read all the lines of the reply from server and print them to Serial 158 | while (client.available()) { 159 | String line = client.readStringUntil('\n'); 160 | line.toCharArray(log, 80); 161 | addLog(LOG_LEVEL_DEBUG_MORE, log); 162 | if (line.substring(0, 15) == "HTTP/1.1 200 OK") 163 | { 164 | strcpy_P(log, PSTR("HTTP : Succes!")); 165 | addLog(LOG_LEVEL_DEBUG, log); 166 | success = true; 167 | } 168 | delay(1); 169 | } 170 | strcpy_P(log, PSTR("HTTP : closing connection")); 171 | addLog(LOG_LEVEL_DEBUG, log); 172 | 173 | client.flush(); 174 | client.stop(); 175 | } 176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /_P100_SRF01.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //#################### Plugin 100 SRF01 Ultrasonic Distance Sensor ###################################### 3 | //####################################################################################################### 4 | 5 | #include 6 | 7 | #define PLUGIN_100 8 | #define PLUGIN_ID_100 100 9 | #define PLUGIN_NAME_100 "Ultrasonic Sensor - SRF01" 10 | #define PLUGIN_VALUENAME1_100 "Distance" 11 | 12 | #define PLUGIN_100_srfAddress 0x01 // Address of the SFR01, default ist 0x01 13 | #define PLUGIN_100_getSoft 0x5D // Byte to tell SRF01 we wish to read software version 14 | #define PLUGIN_100_getRange 0x54 // Byte used to get range from SRF01 in cm 15 | #define PLUGIN_100_getStatus 0x5F // Byte used to get the status of the transducer 16 | 17 | SoftwareSerial *Plugin_100_SRF; 18 | 19 | uint8_t Plugin_100_SRF_Pin; 20 | 21 | boolean Plugin_100(byte function, struct EventStruct *event, String& string) 22 | { 23 | boolean success = false; 24 | byte timeout; 25 | 26 | switch (function) 27 | { 28 | case PLUGIN_DEVICE_ADD: 29 | { 30 | Device[++deviceCount].Number = PLUGIN_ID_100; 31 | Device[deviceCount].Type = DEVICE_TYPE_SINGLE; 32 | Device[deviceCount].VType = SENSOR_TYPE_SINGLE; 33 | Device[deviceCount].Ports = 0; 34 | Device[deviceCount].PullUpOption = false; 35 | Device[deviceCount].InverseLogicOption = false; 36 | Device[deviceCount].FormulaOption = true; 37 | Device[deviceCount].ValueCount = 1; 38 | Device[deviceCount].SendDataOption = true; 39 | break; 40 | } 41 | 42 | case PLUGIN_GET_DEVICENAME: 43 | { 44 | string = F(PLUGIN_NAME_100); 45 | break; 46 | } 47 | 48 | case PLUGIN_GET_DEVICEVALUENAMES: 49 | { 50 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_100)); 51 | break; 52 | } 53 | 54 | 55 | case PLUGIN_INIT: 56 | { 57 | addLog(LOG_LEVEL_INFO, (char*)"INIT : SRF01"); 58 | Plugin_100_SRF_Pin = Settings.TaskDevicePin1[event->TaskIndex]; 59 | Plugin_100_SRF = new SoftwareSerial(Plugin_100_SRF_Pin, Plugin_100_SRF_Pin); 60 | Plugin_100_SRF01_Cmd(PLUGIN_100_srfAddress, PLUGIN_100_getSoft); 61 | for (timeout = 0; timeout < 10; timeout++) { 62 | if (Plugin_100_SRF->available() < 1 ) { 63 | delay(10); 64 | } else { 65 | int softVer = Plugin_100_SRF->read(); 66 | String log = F("SRF01 : Firmware-Version: "); 67 | log += softVer; 68 | addLog(LOG_LEVEL_INFO, log); 69 | success = true; 70 | return success; 71 | }; 72 | }; 73 | addLog(LOG_LEVEL_ERROR, (char*)"SRF01-Init: protocol timeout!"); 74 | success = false; 75 | break; 76 | } 77 | 78 | case PLUGIN_READ: 79 | { 80 | Plugin_100_SRF01_Cmd(PLUGIN_100_srfAddress, PLUGIN_100_getRange); 81 | for (timeout = 0; timeout < 10; timeout++) { 82 | if (Plugin_100_SRF->available() < 2 ) { 83 | delay(10); 84 | } else { 85 | byte highByte = Plugin_100_SRF->read(); 86 | byte lowByte = Plugin_100_SRF->read(); 87 | int dist = ((highByte << 8) + lowByte); 88 | UserVar[event->BaseVarIndex] = dist; 89 | String log = F("SRF1 : Distance: "); 90 | log += dist; 91 | addLog(LOG_LEVEL_INFO, log); 92 | success = true; 93 | return success; 94 | }; 95 | }; 96 | addLog(LOG_LEVEL_ERROR, (char*)"SRF01 : protocol timeout!"); 97 | UserVar[event->BaseVarIndex] = NAN; 98 | success = false; 99 | break; 100 | } 101 | 102 | } 103 | return success; 104 | } 105 | 106 | //**************************************************************************/ 107 | // Send SRF01 Command 108 | //**************************************************************************/ 109 | void Plugin_100_SRF01_Cmd(byte Address, byte cmd) { 110 | Plugin_100_SRF->flush(); 111 | pinMode(Plugin_100_SRF_Pin, OUTPUT); 112 | digitalWrite(Plugin_100_SRF_Pin, LOW); 113 | delay(2); 114 | digitalWrite(Plugin_100_SRF_Pin, HIGH); 115 | delay(1); 116 | Plugin_100_SRF->write(Address); 117 | Plugin_100_SRF->write(cmd); 118 | pinMode(Plugin_100_SRF_Pin, INPUT); 119 | Plugin_100_SRF->flush(); 120 | } 121 | 122 | -------------------------------------------------------------------------------- /_P102_Nodo.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //#################################### Plugin 102: Nodo Event Bridge V2 ################################# 3 | //####################################################################################################### 4 | 5 | #define PLUGIN_102 6 | #define PLUGIN_ID_102 102 7 | #define PLUGIN_NAME_102 "Nodo Event Bridge V2" 8 | #define PLUGIN_VALUENAME1_102 "" 9 | 10 | boolean Plugin_102(byte function, struct EventStruct *event, String& string) 11 | { 12 | boolean success = false; 13 | static byte unicastTargetUnit = 0; 14 | 15 | switch (function) 16 | { 17 | 18 | case PLUGIN_DEVICE_ADD: 19 | { 20 | Device[++deviceCount].Number = PLUGIN_ID_102; 21 | Device[deviceCount].Custom = true; 22 | break; 23 | } 24 | 25 | case PLUGIN_GET_DEVICENAME: 26 | { 27 | string = F(PLUGIN_NAME_102); 28 | break; 29 | } 30 | 31 | case PLUGIN_GET_DEVICEVALUENAMES: 32 | { 33 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_102)); 34 | break; 35 | } 36 | 37 | case PLUGIN_UDP_IN: 38 | { 39 | // event->Data is pointer to UDP buffer 40 | if (event->Data[0] == 255 && event->Data[1] == 254) 41 | { 42 | if (event->Data[11] == 0) // normal traffic, Nodo flags are 0 43 | { 44 | unicastTargetUnit = 0; 45 | } 46 | else 47 | { 48 | unicastTargetUnit = event->Data[9]; // remote source Nodo unit will be unicast target 49 | } 50 | for (byte x = 0; x < 16; x++) 51 | Serial.write(event->Data[x]); 52 | } 53 | success = true; 54 | break; 55 | } 56 | 57 | case PLUGIN_SERIAL_IN: 58 | { 59 | if (Serial.peek() == 255) 60 | { 61 | delay(20); // wait for message to complete 62 | if (Serial.read() == 255 && Serial.read() == 254) 63 | { 64 | byte data[16]; 65 | data[0] = 255; 66 | data[1] = 254; 67 | byte count = 2; 68 | while (Serial.available() && count < 16) 69 | data[count++] = Serial.read(); 70 | 71 | IPAddress sendIP(255, 255, 255, 255); 72 | 73 | if (data[11] != 0) // flags set, set to unicast mode 74 | { 75 | if ((unicastTargetUnit != 0) && (Nodes[unicastTargetUnit].ip[0] != 0)) 76 | for(byte x=0; x <4; x++) 77 | sendIP[x] = Nodes[unicastTargetUnit].ip[x]; 78 | } 79 | 80 | portUDP.beginPacket(sendIP, Settings.UDPPort); 81 | portUDP.write(data, 16); 82 | portUDP.endPacket(); 83 | } 84 | success = true; 85 | } 86 | break; 87 | } 88 | 89 | } 90 | return success; 91 | } 92 | 93 | -------------------------------------------------------------------------------- /_P104_SRF02.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //######################### Plugin 104: SRF02 Ultrasonic range finder sensor ############################ 3 | //####################################################################################################### 4 | 5 | #define PLUGIN_104 6 | #define PLUGIN_ID_104 104 7 | #define PLUGIN_NAME_104 "Ultrasonic range finder - SRF02" 8 | #define PLUGIN_VALUENAME1_104 "DISTANCE" 9 | 10 | #define SRF02_ADDRESS (0x70) // default address (0x70 = datasheet address 0xE0) 11 | 12 | #define SRF02_REG_COMMAND (0x00) // a read on this register returns the software revision 13 | #define SRF02_REG_UNUSED (0x01) 14 | #define SRF02_REG_RANGE_HIGH_BYTE (0x02) 15 | #define SRF02_REG_RANGE_LOW_BYTE (0x03) 16 | #define SRF02_REG_AUTOTUNE_MINIMUM_HIGH_BYTE (0x04) 17 | #define SRF02_REG_AUTOTUNE_MINIMUM_LOW_BYTE (0x05) 18 | 19 | #define SRF02_CMD_REAL_RANGING_MODE_INCH (0x50) 20 | #define SRF02_CMD_REAL_RANGING_MODE_CM (0x51) 21 | #define SRF02_CMD_REAL_RANGING_MODE_US (0x52) 22 | 23 | #define SRF02_CMD_FAKE_RANGING_MODE_INCH (0x56) 24 | #define SRF02_CMD_FAKE_RANGING_MODE_CM (0x57) 25 | #define SRF02_CMD_FAKE_RANGING_MODE_US (0x58) 26 | 27 | #define SRF02_CMD_FORCE_AUTOTUNE_RESTART (0x5C) 28 | 29 | #define SRF02_CMD_I2C_CHANGE_SEQ_1 (0xA0) 30 | #define SRF02_CMD_I2C_CHANGE_SEQ_2 (0xA5) 31 | #define SRF02_CMD_I2C_CHANGE_SEQ_3 (0xAA) 32 | 33 | 34 | uint8_t SRF02_i2caddr; 35 | 36 | boolean Plugin_104(byte function, struct EventStruct *event, String& string) 37 | { 38 | boolean success = false; 39 | 40 | switch (function) 41 | { 42 | 43 | case PLUGIN_DEVICE_ADD: 44 | { 45 | Device[++deviceCount].Number = PLUGIN_ID_104; 46 | Device[deviceCount].Type = DEVICE_TYPE_I2C; 47 | Device[deviceCount].VType = SENSOR_TYPE_SINGLE; 48 | Device[deviceCount].Ports = 0; 49 | Device[deviceCount].PullUpOption = false; 50 | Device[deviceCount].InverseLogicOption = false; 51 | Device[deviceCount].FormulaOption = true; 52 | Device[deviceCount].TimerOption = true; 53 | Device[deviceCount].TimerOptional = true; 54 | Device[deviceCount].ValueCount = 1; 55 | Device[deviceCount].SendDataOption = true; 56 | break; 57 | } 58 | 59 | case PLUGIN_GET_DEVICENAME: 60 | { 61 | string = F(PLUGIN_NAME_104); 62 | break; 63 | } 64 | 65 | case PLUGIN_GET_DEVICEVALUENAMES: 66 | { 67 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_104)); 68 | break; 69 | } 70 | 71 | case PLUGIN_WEBFORM_LOAD: 72 | { 73 | success = true; 74 | break; 75 | } 76 | 77 | case PLUGIN_WEBFORM_SAVE: 78 | { 79 | success = true; 80 | break; 81 | } 82 | 83 | case PLUGIN_INIT: 84 | { 85 | Plugin_104_begin(); 86 | success = true; 87 | break; 88 | } 89 | 90 | case PLUGIN_READ: 91 | { 92 | float value; 93 | value = Plugin_104_getDistance(); 94 | UserVar[event->BaseVarIndex] = value; 95 | String log = F("P104 : distance = "); 96 | log += value; 97 | log += F(" mm"); 98 | addLog(LOG_LEVEL_INFO,log); 99 | success = true; 100 | break; 101 | } 102 | } 103 | return success; 104 | } 105 | 106 | //**************************************************************************/ 107 | // I2C single byte write 108 | //**************************************************************************/ 109 | void Plugin_104_wireWriteByte(uint8_t reg, uint8_t value) 110 | { 111 | Wire.beginTransmission(SRF02_i2caddr); 112 | Wire.write(reg); 113 | Wire.write(value); 114 | Wire.endTransmission(); 115 | } 116 | 117 | //**************************************************************************/ 118 | // I2C two byte read 119 | //**************************************************************************/ 120 | void Plugin_104_wireReadTwoBytes(uint8_t reg, uint16_t *value) 121 | { 122 | Wire.beginTransmission(SRF02_i2caddr); 123 | Wire.write(reg); 124 | Wire.endTransmission(); 125 | 126 | delayMicroseconds(10); 127 | 128 | Wire.requestFrom(SRF02_i2caddr, (uint8_t)2); 129 | *value = ((Wire.read() << 8) | Wire.read()); 130 | } 131 | 132 | //**************************************************************************/ 133 | // Sensor setup 134 | //**************************************************************************/ 135 | void Plugin_104_begin(void) 136 | { 137 | SRF02_i2caddr = SRF02_ADDRESS; 138 | } 139 | 140 | //**************************************************************************/ 141 | // Report distance 142 | //**************************************************************************/ 143 | float Plugin_104_getDistance() 144 | { 145 | uint16_t value; 146 | 147 | Plugin_104_wireWriteByte(SRF02_REG_COMMAND, SRF02_CMD_REAL_RANGING_MODE_US); 148 | delay(70); // transmit -> receive turnaround time (up to 65ms) 149 | Plugin_104_wireReadTwoBytes(SRF02_REG_RANGE_HIGH_BYTE, &value); 150 | 151 | return (float)value/(float)5.82750583; // distance in [mm] (2*1000/343.2) 152 | } 153 | -------------------------------------------------------------------------------- /_P106_IRTX.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //#################################### Plugin 106: Output IR ############################################ 3 | //####################################################################################################### 4 | 5 | // Moved to ESPEasy as P035 (R124) 6 | -------------------------------------------------------------------------------- /_P107_Email_Demo.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //#################################### Plugin 107: Email (Demo) ######################################### 3 | //####################################################################################################### 4 | 5 | // This is just a demo plugin. It does send an email after pressing the "Submit" button, but that's all it can do! 6 | // Needs to be expanded in some way to be useful. 7 | 8 | #define PLUGIN_107 9 | #define PLUGIN_ID_107 107 10 | #define PLUGIN_NAME_107 "Notify - Email" 11 | #define PLUGIN_VALUENAME1_107 "email" 12 | 13 | #define PLUGIN_107_TIMEOUT 3000 14 | 15 | struct P107Struct 16 | { 17 | char server[64]; 18 | byte port; 19 | char domain[64]; 20 | char to[64]; 21 | char from[64]; 22 | char subject[64]; 23 | char body[64]; 24 | }; 25 | 26 | boolean Plugin_107(byte function, struct EventStruct *event, String& string) 27 | { 28 | boolean success = false; 29 | 30 | switch (function) 31 | { 32 | 33 | case PLUGIN_DEVICE_ADD: 34 | { 35 | Device[++deviceCount].Number = PLUGIN_ID_107; 36 | Device[deviceCount].Type = DEVICE_TYPE_SINGLE; 37 | Device[deviceCount].Custom = true; 38 | Device[deviceCount].TimerOption = false; 39 | break; 40 | } 41 | 42 | case PLUGIN_GET_DEVICENAME: 43 | { 44 | string = F(PLUGIN_NAME_107); 45 | break; 46 | } 47 | 48 | case PLUGIN_GET_DEVICEVALUENAMES: 49 | { 50 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_107)); 51 | break; 52 | } 53 | 54 | case PLUGIN_WEBFORM_LOAD: 55 | { 56 | struct P107Struct email; 57 | LoadCustomTaskSettings(event->TaskIndex, (byte*)&email, sizeof(email)); 58 | string += F("Server:"); 61 | string += F("Server port:"); 64 | string += F("Domain:"); 67 | string += F("Mail To:"); 70 | string += F("Mail From:"); 73 | string += F("Subject:"); 76 | string += F("Body:"); 79 | success = true; 80 | break; 81 | } 82 | 83 | case PLUGIN_WEBFORM_SAVE: 84 | { 85 | struct P107Struct email; 86 | String plugin1 = WebServer.arg("Plugin_107_server"); 87 | strncpy(email.server, plugin1.c_str(), sizeof(email.server)); 88 | String plugin2 = WebServer.arg("Plugin_107_port"); 89 | email.port = plugin2.toInt(); 90 | String plugin3 = WebServer.arg("Plugin_107_domain"); 91 | strncpy(email.domain, plugin3.c_str(), sizeof(email.domain)); 92 | String plugin4 = WebServer.arg("Plugin_107_to"); 93 | strncpy(email.to, plugin4.c_str(), sizeof(email.to)); 94 | String plugin5 = WebServer.arg("Plugin_107_from"); 95 | strncpy(email.from, plugin5.c_str(), sizeof(email.from)); 96 | String plugin6 = WebServer.arg("Plugin_107_subject"); 97 | strncpy(email.subject, plugin6.c_str(), sizeof(email.subject)); 98 | String plugin7 = WebServer.arg("Plugin_107_subject"); 99 | strncpy(email.body, plugin7.c_str(), sizeof(email.body)); 100 | 101 | Settings.TaskDeviceID[event->TaskIndex] = 1; // temp fix, needs a dummy value 102 | 103 | SaveCustomTaskSettings(event->TaskIndex, (byte*)&email, sizeof(email)); 104 | success = true; 105 | break; 106 | } 107 | 108 | case PLUGIN_INIT: 109 | { 110 | struct P107Struct email; 111 | LoadCustomTaskSettings(event->TaskIndex, (byte*)&email, sizeof(email)); 112 | Plugin_107_send(email.domain,email.to,email.from,email.subject,email.body,email.server,email.port); 113 | break; 114 | } 115 | 116 | case PLUGIN_WRITE: 117 | { 118 | String tmpString = string; 119 | int argIndex = tmpString.indexOf(','); 120 | if (argIndex) 121 | tmpString = tmpString.substring(0, argIndex); 122 | if (tmpString.equalsIgnoreCase("email")) 123 | { 124 | event->TaskIndex=event->Par1-1; 125 | struct P107Struct email; 126 | LoadCustomTaskSettings(event->TaskIndex, (byte*)&email, sizeof(email)); 127 | Plugin_107_send(email.domain,email.to,email.from,email.subject,email.body,email.server,email.port); 128 | success = true; 129 | } 130 | break; 131 | } 132 | } 133 | return success; 134 | } 135 | 136 | 137 | boolean Plugin_107_send(String aDomain , String aTo, String aFrom, String aSub, String aMesg, String aHost, int aPort) 138 | { 139 | boolean myStatus = false; 140 | 141 | // Use WiFiClient class to create TCP connections 142 | WiFiClient client; 143 | if (!client.connect(aHost.c_str(), aPort)) { 144 | myStatus = false; 145 | } 146 | else { 147 | 148 | // Wait for Client to Start Sending 149 | // The MTA Exchange 150 | while (true) { 151 | 152 | if (Plugin_107_MTA(client, "", "220 ") == false) break; 153 | if (Plugin_107_MTA(client, "EHLO " + aDomain, "250 ") == false) break; 154 | if (Plugin_107_MTA(client, "MAIL FROM:" + aFrom + "", "250 ") == false) break; 155 | if (Plugin_107_MTA(client, "RCPT TO:" + aTo + "", "250 ") == false) break; 156 | if (Plugin_107_MTA(client, "DATA", "354 ") == false) break; 157 | if (Plugin_107_MTA(client, "Subject:" + aSub + "\r\n\r\n" + aMesg + "\r\n.\r\n", "250 ") == false) break; 158 | 159 | myStatus = true; 160 | break; 161 | 162 | } 163 | 164 | client.flush(); 165 | client.stop(); 166 | 167 | if (myStatus == true) { 168 | Serial.println(" Connection Closed Successfully"); 169 | } 170 | else { 171 | Serial.println(" Connection Closed With Error"); 172 | } 173 | 174 | } 175 | 176 | return myStatus; 177 | 178 | } 179 | 180 | 181 | boolean Plugin_107_MTA(WiFiClient client, String aStr, String aWaitForPattern) 182 | { 183 | 184 | boolean myStatus = false; 185 | 186 | if (aStr.length() ) client.println(aStr); 187 | 188 | yield(); 189 | 190 | // Wait For Response 191 | unsigned long ts = millis(); 192 | while (true) { 193 | if ( ts + PLUGIN_107_TIMEOUT < millis() ) { 194 | myStatus = false; 195 | break; 196 | } 197 | 198 | yield(); 199 | 200 | String line = client.readStringUntil('\n'); 201 | 202 | if (line.indexOf(aWaitForPattern) >= 0) { 203 | myStatus = true; 204 | break; 205 | } 206 | } 207 | 208 | return myStatus; 209 | } 210 | 211 | -------------------------------------------------------------------------------- /_P108_WOL.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //#################################### Plugin 108: WOL receiver ######################################### 3 | //####################################################################################################### 4 | 5 | // This plugin receives Wake On Lan (WOL) messages that can be used to power on a connected device. 6 | // Note that the ESP itself has no WOL feature. It must be active to receive this message. 7 | // So you can't wake an ESP from deepsleep using WOL because Wifi is down during deepsleep. 8 | 9 | #define PLUGIN_108 10 | #define PLUGIN_ID_108 108 11 | #define PLUGIN_NAME_108 "WOL Receiver" 12 | #define PLUGIN_VALUENAME1_108 "WOL" 13 | 14 | WiFiUDP *WOL; 15 | 16 | boolean Plugin_108(byte function, struct EventStruct *event, String& string) 17 | { 18 | boolean success = false; 19 | 20 | switch (function) 21 | { 22 | 23 | case PLUGIN_DEVICE_ADD: 24 | { 25 | Device[++deviceCount].Number = PLUGIN_ID_108; 26 | Device[deviceCount].Type = DEVICE_TYPE_SINGLE; 27 | Device[deviceCount].Custom = true; 28 | Device[deviceCount].TimerOption = false; 29 | break; 30 | } 31 | 32 | case PLUGIN_GET_DEVICENAME: 33 | { 34 | string = F(PLUGIN_NAME_108); 35 | break; 36 | } 37 | 38 | case PLUGIN_GET_DEVICEVALUENAMES: 39 | { 40 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_108)); 41 | break; 42 | } 43 | 44 | case PLUGIN_WEBFORM_LOAD: 45 | { 46 | string += F("Power control pin:"); 47 | addPinSelect(false, string, "taskdevicepin1", Settings.TaskDevicePin1[event->TaskIndex]); 48 | 49 | byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0]; 50 | String options[2]; 51 | options[0] = F("Active Low"); 52 | options[1] = F("Active High"); 53 | int optionValues[2]; 54 | optionValues[0] = 0; 55 | optionValues[1] = 1; 56 | string += F("Output state:"); 69 | 70 | success = true; 71 | break; 72 | } 73 | 74 | case PLUGIN_WEBFORM_SAVE: 75 | { 76 | String plugin1 = WebServer.arg("plugin_108_state"); 77 | Settings.TaskDevicePluginConfig[event->TaskIndex][0] = plugin1.toInt(); 78 | success = true; 79 | break; 80 | } 81 | 82 | case PLUGIN_INIT: 83 | { 84 | if (!WOL) 85 | { 86 | WOL = new WiFiUDP; 87 | WOL->begin(7); 88 | } 89 | if (Settings.TaskDevicePin1[event->TaskIndex] != -1) 90 | { 91 | pinMode(Settings.TaskDevicePin1[event->TaskIndex], OUTPUT); 92 | digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], !Settings.TaskDevicePluginConfig[event->TaskIndex][0]); 93 | } 94 | } 95 | success = true; 96 | break; 97 | 98 | case PLUGIN_ONCE_A_SECOND: 99 | { 100 | int packetSize = WOL->parsePacket(); 101 | if (packetSize) 102 | { 103 | statusLED(true); 104 | char packetBuffer[128]; 105 | int len = WOL->read(packetBuffer, 128); 106 | byte match = 0; 107 | uint8_t mac[] = {0, 0, 0, 0, 0, 0}; 108 | uint8_t* macread = WiFi.macAddress(mac); 109 | for (byte x = 0; x < 10; x++) 110 | if (packetBuffer[x + 6] == macread[x]) 111 | match++; 112 | if (match == 6) 113 | { 114 | String log = F("WOL : Magic packet received!"); 115 | addLog(LOG_LEVEL_INFO, log); 116 | if (Settings.TaskDevicePin1[event->TaskIndex] != -1) 117 | { 118 | String log = F("WOL : Power cycle using pin: "); 119 | log += Settings.TaskDevicePin1[event->TaskIndex]; 120 | addLog(LOG_LEVEL_INFO, log); 121 | digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], Settings.TaskDevicePluginConfig[event->TaskIndex][0]); 122 | delay(500); 123 | digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], !Settings.TaskDevicePluginConfig[event->TaskIndex][0]); 124 | } 125 | } 126 | } 127 | success = true; 128 | break; 129 | } 130 | } 131 | return success; 132 | } 133 | 134 | -------------------------------------------------------------------------------- /_P114_DSM501.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //#################################### Plugin 114: DMS501A ############################################# 3 | //#################################### by serpa ############################################# 4 | //####################################################################################################### 5 | 6 | #define PLUGIN_114 7 | #define PLUGIN_ID_114 114 8 | #define PLUGIN_NAME_114 "Dust sensor - DSM501a" 9 | #define PLUGIN_VALUENAME1_114 "PM1.0" // from the datasheet the detection is from PM1 and up. You could have from PM1 to PM2.5, on subtracting PM2.5 value on PM1 value. This value come from the pin #4 10 | #define PLUGIN_VALUENAME2_114 "PM2.5" // from the datasheet the detection is from PM2.5 and up. This value come from the pin #2. With different resistor topn the pin #1, you could adjust the size threshold detection 11 | 12 | unsigned long Plugin_114_pulseCounter[TASKS_MAX]; 13 | unsigned long Plugin_114_pulseTotalCounter[TASKS_MAX]; 14 | unsigned long Plugin_114_pulseTime[TASKS_MAX]; 15 | unsigned long Plugin_114_pulseTimePrevious[TASKS_MAX]; 16 | unsigned long tstart1, tstart2; 17 | unsigned long tduration = 30000; // duration of measurement in ms 18 | //unsigned long triggerOn; // start of pulse time in us 19 | //unsigned long triggerOff; // end of pulse time in us 20 | //unsigned long lowpulseoccupancy; // duration of pulse in us 21 | volatile unsigned long thigh1, thigh2; 22 | volatile unsigned long tlow1, tlow2; 23 | volatile unsigned long startlow1, startlow2; 24 | volatile unsigned long starthigh1, starthigh2; 25 | volatile boolean done1, done2; 26 | volatile boolean value1, value2; 27 | //boolean trigger = false; 28 | volatile float ratio1, ratio2; 29 | volatile int DMSpin1, DMSpin2; 30 | float dens1, dens2; 31 | boolean Plugin_114(byte function, struct EventStruct *event, String& string) 32 | { 33 | boolean success = false; 34 | 35 | switch (function) 36 | { 37 | 38 | case PLUGIN_DEVICE_ADD: 39 | { 40 | Device[++deviceCount].Number = PLUGIN_ID_114; 41 | Device[deviceCount].Type = DEVICE_TYPE_DUAL; 42 | Device[deviceCount].VType = SENSOR_TYPE_TEMP_HUM; 43 | Device[deviceCount].Ports = 0; 44 | Device[deviceCount].PullUpOption = false; 45 | Device[deviceCount].InverseLogicOption = false; 46 | Device[deviceCount].FormulaOption = true; 47 | Device[deviceCount].ValueCount = 2; 48 | Device[deviceCount].SendDataOption = true; 49 | Device[deviceCount].TimerOption = true; 50 | break; 51 | } 52 | 53 | case PLUGIN_GET_DEVICENAME: 54 | { 55 | string = F(PLUGIN_NAME_114); 56 | break; 57 | } 58 | 59 | case PLUGIN_GET_DEVICEVALUENAMES: 60 | { 61 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_114)); 62 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_114)); 63 | break; 64 | } 65 | 66 | case PLUGIN_WEBFORM_LOAD: 67 | { 68 | char tmpString[128]; 69 | sprintf_P(tmpString, PSTR("Averaging Time (mSec):"), Settings.TaskDevicePluginConfig[event->TaskIndex][0]); 70 | addHtml(tmpString); 71 | success = true; 72 | break; 73 | } 74 | 75 | case PLUGIN_WEBFORM_SAVE: 76 | { 77 | String plugin1 = web_server.arg("plugin_114"); 78 | Settings.TaskDevicePluginConfig[event->TaskIndex][0] = plugin1.toInt(); 79 | // tduration= Settings.TaskDevicePluginConfig[event->TaskIndex][0]; 80 | success = true; 81 | break; 82 | } 83 | 84 | 85 | case PLUGIN_INIT: 86 | { 87 | String log = F("INIT : DSM501A "); 88 | log += Settings.TaskDevicePin1[event->TaskIndex]; 89 | addLog(LOG_LEVEL_INFO, log); 90 | // tduration= Settings.TaskDevicePluginConfig[event->TaskIndex][0]; 91 | tstart1 = millis(); 92 | startlow1 = micros(); 93 | starthigh1 = startlow1; 94 | DMSpin1 = Settings.TaskDevicePin1[event->TaskIndex]; 95 | pinMode(DMSpin1, INPUT); 96 | attachInterrupt(digitalPinToInterrupt(DMSpin1), Plugin_114_ISR1, CHANGE); 97 | tstart2 = millis(); 98 | startlow2 = micros(); 99 | starthigh2 = startlow2; 100 | DMSpin2 = Settings.TaskDevicePin2[event->TaskIndex]; 101 | pinMode(DMSpin2, INPUT); 102 | attachInterrupt(digitalPinToInterrupt(DMSpin2), Plugin_114_ISR2, CHANGE); 103 | success = true; 104 | break; 105 | } 106 | 107 | case PLUGIN_READ: 108 | { 109 | if (done1 && done2) { 110 | done1 = FALSE; 111 | dens1 = ratio1 * 110; // ug/m^3 112 | done2 = FALSE; 113 | dens2 = ratio2 * 110; // ug/m^3 114 | 115 | String log = F("DSM501A: PM1.0="); 116 | log += dens1; 117 | log += F(" PM2.5="); 118 | log += dens2; 119 | addLog(LOG_LEVEL_INFO, log); 120 | UserVar[event->BaseVarIndex] = (float) dens1; 121 | UserVar[event->BaseVarIndex + 1] = (float) dens2; 122 | } 123 | success = true; 124 | break; 125 | } 126 | } 127 | return success; 128 | } 129 | 130 | 131 | /*********************************************************************************************\ 132 | Check Pulse (called from irq handler) 133 | \*********************************************************************************************/ 134 | void Plugin_114_ISR1() 135 | { 136 | value1 = digitalRead(DMSpin1); //read input pin just changed 137 | if (value1 == 0) { // gone low 138 | startlow1 = micros(); // record starting of low period 139 | thigh1 += startlow1 - starthigh1; // record duration of past high state 140 | } else { // gone high 141 | starthigh1 = micros(); // record starting of high period 142 | tlow1 += starthigh1 - startlow1; // record duration of past low state 143 | } 144 | if (millis() > tstart1 + tduration) { // check if average time has past 145 | tstart1 = millis(); // reset time period 146 | ratio1 = float(tlow1) / float(thigh1 + tlow1) * 100; // compute ratio low to total 147 | tlow1 = 0; // reset low time counter 148 | thigh1 = 0; // reset high time counter 149 | done1 = TRUE; // set reading complete flag 150 | } 151 | } 152 | /*********************************************************************************************\ 153 | Check Pulse (called from irq handler) 154 | \*********************************************************************************************/ 155 | void Plugin_114_ISR2() 156 | { 157 | value2 = digitalRead(DMSpin2); //read input pin just changed 158 | if (value2 == 0) { // gone low 159 | startlow2 = micros(); // record starting of low period 160 | thigh2 += startlow2 - starthigh2; // record duration of past high state 161 | } else { // gone high 162 | starthigh2 = micros(); // record starting of high period 163 | tlow2 += starthigh2 - startlow2; // record duration of past low state 164 | } 165 | if (millis() > tstart2 + tduration) { // check if average time has past 166 | tstart2 = millis(); // reset time period 167 | ratio2 = float(tlow2) / float(thigh2 + tlow2) * 100; // compute ratio low to high 168 | tlow2 = 0; // reset low time counter 169 | thigh2 = 0; // reset high time counter 170 | done2 = TRUE; // set reading complete flag 171 | } 172 | } 173 | 174 | /////////////////////////////////////////////////////////////////////////////////////////////////////// 175 | -------------------------------------------------------------------------------- /_P115_HeatpumpIR.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //#################################### Plugin 115: Heatpump IR ########################################## 3 | //####################################################################################################### 4 | 5 | /* 6 | 7 | HeatpumpIR is now an official ESPEasy plugin, with plugin number 086. Supports both ESP8266 and ESP32. 8 | 9 | For ESP8266, use the minimal_IRExt image 10 | 11 | For ESP32, you need to build the image yourself: 12 | 13 | platformio run --target upload --environment test_ESP32_IRExt_4M316k 14 | 15 | */ -------------------------------------------------------------------------------- /_P116_ID12.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //#################################### Plugin 116: Serial RFID ID-12 #################################### 3 | //####################################################################################################### 4 | 5 | #define PLUGIN_116 6 | #define PLUGIN_ID_116 116 7 | #define PLUGIN_NAME_116 "RFID Reader - ID12LA" 8 | #define PLUGIN_VALUENAME1_116 "Tag" 9 | 10 | boolean Plugin_116_init = false; 11 | 12 | boolean Plugin_116(byte function, struct EventStruct *event, String& string) 13 | { 14 | boolean success = false; 15 | 16 | switch (function) 17 | { 18 | 19 | case PLUGIN_DEVICE_ADD: 20 | { 21 | Device[++deviceCount].Number = PLUGIN_ID_116; 22 | Device[deviceCount].VType = SENSOR_TYPE_LONG; 23 | Device[deviceCount].Ports = 0; 24 | Device[deviceCount].PullUpOption = false; 25 | Device[deviceCount].InverseLogicOption = false; 26 | Device[deviceCount].FormulaOption = false; 27 | Device[deviceCount].ValueCount = 1; 28 | Device[deviceCount].SendDataOption = true; 29 | Device[deviceCount].TimerOption = false; 30 | Device[deviceCount].GlobalSyncOption = true; 31 | break; 32 | } 33 | 34 | case PLUGIN_GET_DEVICENAME: 35 | { 36 | string = F(PLUGIN_NAME_116); 37 | break; 38 | } 39 | 40 | case PLUGIN_GET_DEVICEVALUENAMES: 41 | { 42 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_116)); 43 | break; 44 | } 45 | 46 | 47 | case PLUGIN_INIT: 48 | { 49 | Plugin_116_init = true; 50 | Serial.begin(9600); 51 | success = true; 52 | break; 53 | } 54 | 55 | 56 | case PLUGIN_SERIAL_IN: 57 | { 58 | if (Plugin_116_init) 59 | { 60 | byte val = 0; 61 | byte code[6]; 62 | byte checksum = 0; 63 | byte bytesread = 0; 64 | byte tempbyte = 0; 65 | 66 | if ((val = Serial.read()) == 2) 67 | { // check for header 68 | bytesread = 0; 69 | while (bytesread < 12) { // read 10 digit code + 2 digit checksum 70 | if ( Serial.available() > 0) { 71 | val = Serial.read(); 72 | if ((val == 0x0D) || (val == 0x0A) || (val == 0x03) || (val == 0x02)) { 73 | // if header or stop bytes before the 10 digit reading 74 | break; 75 | } 76 | 77 | // Do Ascii/Hex conversion: 78 | if ((val >= '0') && (val <= '9')) { 79 | val = val - '0'; 80 | } 81 | else if ((val >= 'A') && (val <= 'F')) { 82 | val = 10 + val - 'A'; 83 | } 84 | 85 | // Every two hex-digits, add byte to code: 86 | if (bytesread & 1 == 1) { 87 | // make some space for this hex-digit by 88 | // shifting the previous hex-digit with 4 bits to the left: 89 | code[bytesread >> 1] = (val | (tempbyte << 4)); 90 | 91 | if (bytesread >> 1 != 5) { // If we're at the checksum byte, 92 | checksum ^= code[bytesread >> 1]; // Calculate the checksum... (XOR) 93 | }; 94 | } 95 | else { 96 | tempbyte = val; // Store the first hex digit first... 97 | }; 98 | bytesread++; // ready to read next digit 99 | } 100 | } 101 | } 102 | 103 | if (bytesread == 12) 104 | { 105 | if (code[5] == checksum) 106 | { 107 | // temp woraround, ESP Easy framework does not currently prepare this... 108 | byte index = 0; 109 | for (byte y = 0; y < TASKS_MAX; y++) 110 | if (Settings.TaskDeviceNumber[y] == PLUGIN_ID_116) 111 | index = y; 112 | byte DeviceIndex = getDeviceIndex(Settings.TaskDeviceNumber[index]); 113 | event->TaskIndex = index; 114 | event->BaseVarIndex = index * VARS_PER_TASK; 115 | event->idx = Settings.TaskDeviceID[index]; 116 | event->sensorType = Device[DeviceIndex].VType; 117 | // endof workaround 118 | 119 | unsigned long key = 0; 120 | for (byte i = 1; i < 5; i++) key = key | (((unsigned long) code[i] << ((4 - i) * 8))); 121 | UserVar[event->BaseVarIndex] = (key & 0xFFFF); 122 | UserVar[event->BaseVarIndex + 1] = ((key >> 16) & 0xFFFF); 123 | String log = F("RFID : Tag: "); 124 | log += key; 125 | addLog(LOG_LEVEL_INFO, log); 126 | sendData(event); 127 | } 128 | } 129 | success = true; 130 | } 131 | break; 132 | } 133 | 134 | } 135 | return success; 136 | } 137 | 138 | -------------------------------------------------------------------------------- /_P119_BME680.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //#################### Plugin 119 BME680 I2C Temp/Hum/Barometric/Pressure/Gas Resistence Sensor ######## 3 | //####################################################################################################### 4 | 5 | /******************************************************************************* 6 | * Copyright 2017 7 | * Written by Rossen Tchobanski (rosko@rosko.net) 8 | * BSD license, all text above must be included in any redistribution 9 | * 10 | * Release notes: 11 | Adafruit_BME680 Library v1.0.5 required (https://github.com/adafruit/Adafruit_BME680/tree/1.0.5) 12 | /******************************************************************************/ 13 | 14 | 15 | //#ifdef PLUGIN_BUILD_DEV 16 | //#ifdef PLUGIN_BUILD_TESTING 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | 24 | 25 | #define PLUGIN_119 26 | #define PLUGIN_ID_119 119 27 | #define PLUGIN_NAME_119 "Environment - BME680" 28 | #define PLUGIN_VALUENAME1_119 "Temperature" 29 | #define PLUGIN_VALUENAME2_119 "Humidity" 30 | #define PLUGIN_VALUENAME3_119 "Pressure" 31 | #define PLUGIN_VALUENAME4_119 "Gas" 32 | 33 | #define BME_SCK 13 34 | #define BME_MISO 12 35 | #define BME_MOSI 11 36 | #define BME_CS 10 37 | 38 | #define SEALEVELPRESSURE_HPA (1013.25) 39 | 40 | Adafruit_BME680 bme; // I2C 41 | //Adafruit_BME680 bme(BME_CS); // hardware SPI 42 | //Adafruit_BME680 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); 43 | 44 | 45 | boolean Plugin_119_init = false; 46 | 47 | boolean Plugin_119(byte function, struct EventStruct *event, String& string) 48 | { 49 | boolean success = false; 50 | 51 | switch (function) 52 | { 53 | case PLUGIN_DEVICE_ADD: 54 | { 55 | Device[++deviceCount].Number = PLUGIN_ID_119; 56 | Device[deviceCount].Type = DEVICE_TYPE_I2C; 57 | Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_QUAD; 58 | Device[deviceCount].Ports = 0; 59 | Device[deviceCount].PullUpOption = false; 60 | Device[deviceCount].InverseLogicOption = false; 61 | Device[deviceCount].FormulaOption = true; 62 | Device[deviceCount].ValueCount = 4; 63 | Device[deviceCount].SendDataOption = true; 64 | Device[deviceCount].TimerOption = true; 65 | Device[deviceCount].GlobalSyncOption = true; 66 | break; 67 | } 68 | 69 | case PLUGIN_GET_DEVICENAME: 70 | { 71 | string = F(PLUGIN_NAME_119); 72 | break; 73 | } 74 | 75 | case PLUGIN_GET_DEVICEVALUENAMES: 76 | { 77 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_119)); 78 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_119)); 79 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_119)); 80 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[3], PSTR(PLUGIN_VALUENAME4_119)); 81 | break; 82 | } 83 | 84 | case PLUGIN_WEBFORM_LOAD: 85 | { 86 | byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0]; 87 | /* 88 | String options[2]; 89 | options[0] = F("0x76 - default settings (SDO Low)"); 90 | options[1] = F("0x77 - alternate settings (SDO HIGH)"); 91 | */ 92 | int optionValues[2] = { 0x77, 0x76 }; 93 | addFormSelectorI2C(F("plugin_119_BME680_i2c"), 2, optionValues, choice); 94 | addFormNote(F("SDO Low=0x76, High=0x77")); 95 | 96 | addFormNumericBox(F("Altitude"), F("plugin_119_BME680_elev"), Settings.TaskDevicePluginConfig[event->TaskIndex][1]); 97 | addUnit(F("m")); 98 | 99 | success = true; 100 | break; 101 | } 102 | 103 | case PLUGIN_WEBFORM_SAVE: 104 | { 105 | Settings.TaskDevicePluginConfig[event->TaskIndex][0] = getFormItemInt(F("plugin_119_BME680_i2c")); 106 | Settings.TaskDevicePluginConfig[event->TaskIndex][1] = getFormItemInt(F("plugin_119_BME680_elev")); 107 | success = true; 108 | break; 109 | } 110 | 111 | case PLUGIN_READ: 112 | { 113 | 114 | 115 | 116 | if (!Plugin_119_init) 117 | { 118 | addLog(LOG_LEVEL_INFO, F("BME680 : init")); 119 | 120 | Plugin_119_init = bme.begin(); 121 | 122 | // Set up oversampling and filter initialization 123 | bme.setTemperatureOversampling(BME680_OS_8X); 124 | bme.setHumidityOversampling(BME680_OS_2X); 125 | bme.setPressureOversampling(BME680_OS_4X); 126 | bme.setIIRFilterSize(BME680_FILTER_SIZE_3); 127 | bme.setGasHeater(320, 150); // 320*C for 150 ms 128 | 129 | success = true; 130 | break; 131 | } 132 | 133 | if (Plugin_119_init) 134 | { 135 | if (! bme.performReading()) { 136 | addLog(LOG_LEVEL_ERROR, F("BME680 : Failed to perform reading!")); 137 | success = false; 138 | break; 139 | } 140 | 141 | UserVar[event->BaseVarIndex + 0] = bme.temperature; 142 | UserVar[event->BaseVarIndex + 1] = bme.humidity; 143 | UserVar[event->BaseVarIndex + 2] = bme.pressure / 100.0; 144 | UserVar[event->BaseVarIndex + 3] = bme.gas_resistance / 1000.0; 145 | 146 | } 147 | 148 | success = true; 149 | break; 150 | } 151 | 152 | } 153 | return success; 154 | } 155 | 156 | //#endif 157 | -------------------------------------------------------------------------------- /_P122_NeoPixel.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //#################################### Plugin 122: NeoPixel clock ####################################### 3 | //####################################################################################################### 4 | 5 | // Command: NeoPixel ,,, 6 | 7 | #include 8 | Adafruit_NeoPixel *Plugin_122_pixels; 9 | 10 | #define PLUGIN_122 11 | #define PLUGIN_ID_122 122 12 | #define PLUGIN_NAME_122 "NeoPixel Basic" 13 | #define PLUGIN_VALUENAME1_122 "" 14 | boolean Plugin_122(byte function, struct EventStruct *event, String& string) 15 | { 16 | boolean success = false; 17 | 18 | switch (function) 19 | { 20 | 21 | case PLUGIN_DEVICE_ADD: 22 | { 23 | Device[++deviceCount].Number = PLUGIN_ID_122; 24 | Device[deviceCount].Type = DEVICE_TYPE_SINGLE; 25 | Device[deviceCount].Custom = true; 26 | Device[deviceCount].TimerOption = false; 27 | break; 28 | } 29 | 30 | case PLUGIN_GET_DEVICENAME: 31 | { 32 | string = F(PLUGIN_NAME_122); 33 | break; 34 | } 35 | 36 | case PLUGIN_GET_DEVICEVALUENAMES: 37 | { 38 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_122)); 39 | break; 40 | } 41 | 42 | case PLUGIN_WEBFORM_LOAD: 43 | { 44 | char tmpString[128]; 45 | sprintf_P(tmpString, PSTR("Led Count:"), Settings.TaskDevicePluginConfig[event->TaskIndex][0]); 46 | string += tmpString; 47 | 48 | string += F("GPIO:"); 49 | addPinSelect(false, string, "taskdevicepin1", Settings.TaskDevicePin1[event->TaskIndex]); 50 | 51 | success = true; 52 | break; 53 | } 54 | 55 | case PLUGIN_WEBFORM_SAVE: 56 | { 57 | String plugin1 = WebServer.arg(F("plugin_122_leds")); 58 | Settings.TaskDevicePluginConfig[event->TaskIndex][0] = plugin1.toInt(); 59 | success = true; 60 | break; 61 | } 62 | 63 | case PLUGIN_INIT: 64 | { 65 | if (!Plugin_122_pixels) 66 | { 67 | Plugin_122_pixels = new Adafruit_NeoPixel(Settings.TaskDevicePluginConfig[event->TaskIndex][0], Settings.TaskDevicePin1[event->TaskIndex], NEO_GRB + NEO_KHZ800); 68 | Plugin_122_pixels->begin(); // This initializes the NeoPixel library. 69 | } 70 | success = true; 71 | break; 72 | } 73 | 74 | case PLUGIN_WRITE: 75 | { 76 | if (Plugin_122_pixels) 77 | { 78 | String tmpString = string; 79 | int argIndex = tmpString.indexOf(','); 80 | if (argIndex) 81 | tmpString = tmpString.substring(0, argIndex); 82 | 83 | if (tmpString.equalsIgnoreCase(F("NeoPixel"))) 84 | { 85 | char Line[80]; 86 | char TmpStr1[80]; 87 | TmpStr1[0] = 0; 88 | string.toCharArray(Line, 80); 89 | int Par4 = 0; 90 | if (GetArgv(Line, TmpStr1, 5)) Par4 = str2int(TmpStr1); 91 | Plugin_122_pixels->setPixelColor(event->Par1 - 1, Plugin_122_pixels->Color(event->Par2, event->Par3, Par4)); 92 | Plugin_122_pixels->show(); // This sends the updated pixel color to the hardware. 93 | success = true; 94 | } 95 | } 96 | break; 97 | } 98 | 99 | } 100 | return success; 101 | } 102 | 103 | -------------------------------------------------------------------------------- /_P126_Ping.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //#################################### Plugin 126: Ping IP (non-blocking) ############################### 3 | //####################################################################################################### 4 | // Plugin originally created by Neutrino 5 | // Updated by Alexander Nagy ( https://bitekmindenhol.blog.hu/ ) 6 | // 7 | // Used Library: https://github.com/bluemurder/esp8266-ping by Alessio Leoncini 8 | // 9 | 10 | #define PLUGIN_126 11 | #define PLUGIN_ID_126 126 12 | #define PLUGIN_NAME_126 "Network - Ping IP Device" 13 | #define PLUGIN_VALUENAME1_126 "Ping" 14 | 15 | #include // ESP8266-ping 16 | 17 | #define P126_MaxInstances 3 // maximal allowed number of P126 devices 18 | Pinger p126_pinger[P126_MaxInstances]; 19 | 20 | boolean Plugin_126_init[P126_MaxInstances]; 21 | 22 | boolean Plugin_126(byte function, struct EventStruct *event, String& string) 23 | { 24 | boolean success = false; 25 | 26 | switch (function) 27 | { 28 | 29 | case PLUGIN_DEVICE_ADD: 30 | { 31 | Device[++deviceCount].Number = PLUGIN_ID_126; 32 | Device[deviceCount].Type = DEVICE_TYPE_DUMMY; 33 | Device[deviceCount].VType = SENSOR_TYPE_SWITCH; 34 | Device[deviceCount].Ports = 0; 35 | Device[deviceCount].PullUpOption = false; 36 | Device[deviceCount].ValueCount = 1; 37 | Device[deviceCount].SendDataOption = true; 38 | Device[deviceCount].TimerOption = true; 39 | Device[deviceCount].GlobalSyncOption = true; 40 | break; 41 | } 42 | 43 | case PLUGIN_GET_DEVICENAME: 44 | { 45 | string = F(PLUGIN_NAME_126); 46 | break; 47 | } 48 | 49 | case PLUGIN_GET_DEVICEVALUENAMES: 50 | { 51 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_126)); 52 | break; 53 | } 54 | 55 | case PLUGIN_WEBFORM_LOAD: 56 | { 57 | addFormNumericBox(F("IP 1st octet"), F("plugin_126_1"), Settings.TaskDevicePluginConfig[event->TaskIndex][1], 1, 254); 58 | addFormNumericBox(F("IP 2nd octet"), F("plugin_126_2"), Settings.TaskDevicePluginConfig[event->TaskIndex][2], 0, 255); 59 | addFormNumericBox(F("IP 3rd octet"), F("plugin_126_3"), Settings.TaskDevicePluginConfig[event->TaskIndex][3], 0, 255); 60 | addFormNumericBox(F("IP 4th octet"), F("plugin_126_4"), Settings.TaskDevicePluginConfig[event->TaskIndex][4], 1, 254); 61 | success = true; 62 | break; 63 | } 64 | 65 | case PLUGIN_WEBFORM_SAVE: 66 | { 67 | LoadTaskSettings(event->TaskIndex); 68 | byte baseaddr = 0; 69 | for (byte TaskIndex = 0; TaskIndex < event->TaskIndex; TaskIndex++) 70 | { 71 | if (Settings.TaskDeviceNumber[TaskIndex] == PLUGIN_ID_126) { 72 | baseaddr = baseaddr + 1; 73 | } 74 | } 75 | success = false; 76 | if (baseaddr < P126_MaxInstances) { 77 | Settings.TaskDevicePluginConfig[event->TaskIndex][0] = baseaddr; 78 | Settings.TaskDevicePluginConfig[event->TaskIndex][1] = getFormItemInt(F("plugin_126_1")); 79 | Settings.TaskDevicePluginConfig[event->TaskIndex][2] = getFormItemInt(F("plugin_126_2")); 80 | Settings.TaskDevicePluginConfig[event->TaskIndex][3] = getFormItemInt(F("plugin_126_3")); 81 | Settings.TaskDevicePluginConfig[event->TaskIndex][4] = getFormItemInt(F("plugin_126_4")); 82 | success = true; 83 | } else { 84 | Settings.TaskDeviceEnabled[event->TaskIndex] = false; 85 | String log = F("Maximum number of Ping devices reached! "); 86 | log += String(baseaddr); 87 | addLog(LOG_LEVEL_INFO, log); 88 | } 89 | break; 90 | } 91 | 92 | case PLUGIN_INIT: 93 | { 94 | LoadTaskSettings(event->TaskIndex); 95 | byte baseaddr = 0; 96 | for (byte TaskIndex = 0; TaskIndex < event->TaskIndex; TaskIndex++) 97 | { 98 | if (Settings.TaskDeviceNumber[TaskIndex] == PLUGIN_ID_126) { 99 | baseaddr = baseaddr + 1; 100 | } 101 | } 102 | success = false; 103 | if (baseaddr < P126_MaxInstances) { 104 | Settings.TaskDevicePluginConfig[event->TaskIndex][0] = baseaddr; 105 | success = true; 106 | Plugin_126_init[baseaddr] = true; 107 | } else { 108 | Settings.TaskDeviceEnabled[event->TaskIndex] = false; 109 | String log = F("Maximum number of Ping devices reached! "); 110 | log += String(baseaddr); 111 | addLog(LOG_LEVEL_INFO, log); 112 | } 113 | break; 114 | } 115 | 116 | case PLUGIN_READ: 117 | { 118 | success = false; 119 | bool ret = false; 120 | LoadTaskSettings(event->TaskIndex); 121 | byte cpy = Settings.TaskDevicePluginConfig[event->TaskIndex][0]; 122 | if (cpy < P126_MaxInstances) { 123 | if (Plugin_126_init[cpy]) 124 | { 125 | if ((Settings.TaskDevicePluginConfig[event->TaskIndex][1] > 0) and (Settings.TaskDevicePluginConfig[event->TaskIndex][4] > 0)) { 126 | IPAddress ip( Settings.TaskDevicePluginConfig[event->TaskIndex][1], Settings.TaskDevicePluginConfig[event->TaskIndex][2], Settings.TaskDevicePluginConfig[event->TaskIndex][3], Settings.TaskDevicePluginConfig[event->TaskIndex][4] ); // The remote ip to ping 127 | String log = F("Ping "); 128 | ret = p126_pinger[cpy].Ping(ip, 3, 100); // 3 probe, 100millisec timeout 129 | p126_pinger[cpy].OnEnd([](const PingerResponse & response) 130 | { 131 | for (byte TaskIndex = 0; TaskIndex < TASKS_MAX; TaskIndex++) 132 | { 133 | if (Settings.TaskDeviceNumber[TaskIndex] == PLUGIN_ID_126) { 134 | if ( 135 | (response.DestIPAddress[0] == Settings.TaskDevicePluginConfig[TaskIndex][1]) and 136 | (response.DestIPAddress[1] == Settings.TaskDevicePluginConfig[TaskIndex][2]) and 137 | (response.DestIPAddress[2] == Settings.TaskDevicePluginConfig[TaskIndex][3]) and 138 | (response.DestIPAddress[3] == Settings.TaskDevicePluginConfig[TaskIndex][4])) { 139 | UserVar[(TaskIndex * VARS_PER_TASK)] = (response.TotalReceivedResponses > 0); 140 | break; 141 | } 142 | 143 | } 144 | } 145 | 146 | String logs = F("Ping result for:"); 147 | logs += response.DestIPAddress.toString().c_str(); 148 | logs += F(" "); 149 | logs += String(response.TotalReceivedResponses); 150 | addLog(LOG_LEVEL_DEBUG, logs); 151 | return true; 152 | }); 153 | success = true; 154 | } 155 | } 156 | } 157 | break; 158 | } 159 | 160 | } 161 | return success; 162 | } 163 | -------------------------------------------------------------------------------- /_P129_RC522_RFID.ino: -------------------------------------------------------------------------------- 1 | #ifdef USES_P129 2 | //####################################################################################################### 3 | //################################ Plugin-214: RC522 SPI RFID reader #################################### 4 | //####################################################################################################### 5 | 6 | #define PLUGIN_129 7 | #define PLUGIN_ID_129 129 8 | #define PLUGIN_NAME_129 "RFID - RC522 SPI" 9 | #define PLUGIN_VALUENAME1_129 "Tag" 10 | // #define PLUGIN_129_CS 16 //needed? 11 | 12 | #include 13 | #include 14 | 15 | MFRC522 mfrc522; 16 | 17 | uint8_t Plugin_129_packetbuffer[64]; 18 | uint8_t Plugin_129_command; 19 | 20 | boolean Plugin_129(byte function, struct EventStruct *event, String& string) 21 | { 22 | boolean success = false; 23 | 24 | 25 | 26 | switch (function) 27 | { 28 | 29 | case PLUGIN_DEVICE_ADD: 30 | { 31 | Device[++deviceCount].Number = PLUGIN_ID_129; 32 | Device[deviceCount].Type = DEVICE_TYPE_SINGLE; 33 | Device[deviceCount].VType = SENSOR_TYPE_LONG; 34 | Device[deviceCount].Ports = 0; 35 | Device[deviceCount].PullUpOption = false; 36 | Device[deviceCount].InverseLogicOption = false; 37 | Device[deviceCount].ValueCount = 1; 38 | Device[deviceCount].SendDataOption = true; 39 | Device[deviceCount].TimerOption = false; 40 | Device[deviceCount].GlobalSyncOption = true; 41 | break; 42 | } 43 | 44 | case PLUGIN_GET_DEVICENAME: 45 | { 46 | string = F(PLUGIN_NAME_129); 47 | break; 48 | } 49 | 50 | case PLUGIN_GET_DEVICEVALUENAMES: 51 | { 52 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_129)); 53 | break; 54 | } 55 | 56 | case PLUGIN_GET_DEVICEGPIONAMES: //define 'GPIO 1st' name in webserver 57 | { 58 | event->String1 = formatGpioName_output(F("CS PIN")); 59 | break; 60 | } 61 | 62 | case PLUGIN_WEBFORM_LOAD: 63 | { 64 | // cant compile addFormPinSelect(string, F("Reset Pin"), F("taskdevicepin3"), Settings.TaskDevicePin3[event->TaskIndex]); 65 | addFormPinSelect(F("Reset Pin"), F("taskdevicepin3"), Settings.TaskDevicePin3[event->TaskIndex]); 66 | success = true; 67 | break; 68 | } 69 | 70 | case PLUGIN_INIT: 71 | { 72 | 73 | for(byte x=0; x < 3; x++) 74 | { 75 | String log = F("MFRC522: Init"); 76 | addLog(LOG_LEVEL_INFO, log); 77 | if(Plugin_129_Init(Settings.TaskDevicePin1[event->TaskIndex],Settings.TaskDevicePin3[event->TaskIndex])) 78 | break; 79 | delay(1000); 80 | } 81 | break; 82 | } 83 | 84 | case PLUGIN_TEN_PER_SECOND: 85 | { 86 | static unsigned long tempcounter = 0; 87 | static byte counter; 88 | static byte errorCount=0; 89 | 90 | counter++; 91 | if (counter == 3) 92 | { 93 | counter = 0; 94 | uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; 95 | uint8_t uidLength; 96 | // byte error = Plugin_129_readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength); 97 | byte error = Plugin_129_readPassiveTargetID(uid, &uidLength); // not defined if plugin P017_PN532 is not selected! 98 | 99 | if (error == 1) 100 | { 101 | errorCount++; 102 | String log = F("MFRC522: Read error: "); 103 | log += errorCount; 104 | addLog(LOG_LEVEL_ERROR, log); 105 | } 106 | else 107 | errorCount=0; 108 | 109 | if (errorCount > 2) // if three consecutive I2C errors, reset PN532 110 | { 111 | Plugin_129_Init(Settings.TaskDevicePin1[event->TaskIndex],Settings.TaskDevicePin3[event->TaskIndex]); 112 | } 113 | 114 | if (error == 0) { 115 | unsigned long key = uid[0]; 116 | for (uint8_t i = 1; i < 4; i++) { 117 | key <<= 8; 118 | key += uid[i]; 119 | } 120 | UserVar[event->BaseVarIndex] = (key & 0xFFFF); 121 | UserVar[event->BaseVarIndex + 1] = ((key >> 16) & 0xFFFF); 122 | String log = F("MFRC522: Tag: "); 123 | log += key; 124 | tempcounter++; 125 | log += " "; 126 | log += tempcounter; 127 | addLog(LOG_LEVEL_INFO, log); 128 | sendData(event); 129 | } 130 | } 131 | break; 132 | } 133 | } 134 | return success; 135 | } 136 | 137 | 138 | /*********************************************************************************************\ 139 | * MFRC522 init 140 | \*********************************************************************************************/ 141 | boolean Plugin_129_Init(int8_t csPin, int8_t resetPin) 142 | { 143 | if (resetPin != -1) 144 | { 145 | String log = F("MFRC522: Reset on pin: "); 146 | log += resetPin; 147 | addLog(LOG_LEVEL_INFO, log); 148 | pinMode(resetPin, OUTPUT); 149 | digitalWrite(resetPin, LOW); 150 | delay(100); 151 | digitalWrite(resetPin, HIGH); 152 | pinMode(resetPin, INPUT_PULLUP); 153 | delay(10); 154 | } 155 | 156 | pinMode(csPin, OUTPUT); 157 | digitalWrite(csPin, LOW); 158 | 159 | 160 | mfrc522.PCD_Init(csPin,resetPin); // Init MFRC522 module 161 | 162 | //If you set Antenna Gain to Max it will increase reading distance 163 | mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_max); 164 | 165 | bool result = mfrc522.PCD_PerformSelfTest(); // perform the test 166 | 167 | if (result) { 168 | //String log = F("RC522: Found"); 169 | // Get the MFRC522 software version 170 | byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg); 171 | 172 | // When 0x00 or 0xFF is returned, communication probably failed 173 | if ((v == 0x00) || (v == 0xFF)) { 174 | String log=F("MFRC522: Communication failure, is the MFRC522 properly connected?"); 175 | addLog(LOG_LEVEL_ERROR,log); 176 | return false; 177 | } 178 | else 179 | { 180 | String log=F("MFRC522: Software Version: "); 181 | if (v == 0x91) 182 | log+=F(" = v1.0"); 183 | else if (v == 0x92) 184 | log+=F(" = v2.0"); 185 | else 186 | log+=F(" (unknown),probably a chinese clone?"); 187 | 188 | addLog(LOG_LEVEL_INFO, log); 189 | } 190 | } 191 | else 192 | return false; 193 | 194 | return true; 195 | } 196 | 197 | 198 | 199 | 200 | /*********************************************************************************************\ 201 | * RC522 read tag ID 202 | \*********************************************************************************************/ 203 | //byte Plugin_129_readPassiveTargetID(uint8_t cardbaudrate, uint8_t *uid, uint8_t *uidLength) 204 | byte Plugin_129_readPassiveTargetID(uint8_t *uid, uint8_t *uidLength) //needed ? see above (not PN532) 205 | { 206 | // Getting ready for Reading PICCs 207 | if ( ! mfrc522.PICC_IsNewCardPresent()) { //If a new PICC placed to RFID reader continue 208 | return 2; 209 | } 210 | String log = F("MFRC522: New Card Detected"); 211 | addLog(LOG_LEVEL_INFO,log); 212 | if ( ! mfrc522.PICC_ReadCardSerial()) { //Since a PICC placed get Serial and continue 213 | return 1; 214 | } 215 | 216 | // There are Mifare PICCs which have 4 byte or 7 byte UID care if you use 7 byte PICC 217 | // I think we should assume every PICC as they have 4 byte UID 218 | // Until we support 7 byte PICCs 219 | log =F("MFRC522: Scanned PICC's UID"); 220 | addLog(LOG_LEVEL_INFO,log); 221 | for ( uint8_t i = 0; i < 4; i++) { // 222 | uid[i] = mfrc522.uid.uidByte[i]; 223 | //Serial.print(readCard[i], HEX); 224 | } 225 | *uidLength = 4; 226 | mfrc522.PICC_HaltA(); // Stop reading 227 | return 0; 228 | 229 | } 230 | 231 | 232 | #endif // USES_P129 233 | -------------------------------------------------------------------------------- /_P131_SHT3X.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //#################### Plugin 131: SHT30/SHT31/SHT35 Temp/Humidity Sensor ############################### 3 | //####################################################################################################### 4 | 5 | class SHT3X{ 6 | public: 7 | SHT3X(uint8_t address); 8 | void get(void); 9 | float cTemp=0; 10 | // float fTemp=0; 11 | float humidity=0; 12 | 13 | private: 14 | uint8_t _address; 15 | }; 16 | 17 | #define PLUGIN_131 18 | #define PLUGIN_ID_131 131 19 | #define PLUGIN_NAME_131 "Temperature & Humidity - SHT3X" 20 | #define PLUGIN_VALUENAME1_131 "Temperature" 21 | #define PLUGIN_VALUENAME2_131 "Humidity" 22 | 23 | boolean Plugin_131_init = false; 24 | 25 | uint8_t addr=0x45; 26 | SHT3X sht30(addr); 27 | 28 | boolean Plugin_131(byte function, struct EventStruct *event, String& string) 29 | { 30 | boolean success = false; 31 | 32 | switch (function) 33 | { 34 | case PLUGIN_DEVICE_ADD: 35 | { 36 | Device[++deviceCount].Number = PLUGIN_ID_131; 37 | Device[deviceCount].Type = DEVICE_TYPE_I2C; 38 | Device[deviceCount].VType = SENSOR_TYPE_TEMP_HUM; 39 | Device[deviceCount].Ports = 0; 40 | Device[deviceCount].PullUpOption = false; 41 | Device[deviceCount].InverseLogicOption = false; 42 | Device[deviceCount].FormulaOption = true; 43 | Device[deviceCount].ValueCount = 2; 44 | Device[deviceCount].SendDataOption = true; 45 | Device[deviceCount].TimerOption = true; 46 | Device[deviceCount].GlobalSyncOption = true; 47 | break; 48 | } 49 | 50 | case PLUGIN_GET_DEVICENAME: 51 | { 52 | string = F(PLUGIN_NAME_131); 53 | break; 54 | } 55 | 56 | case PLUGIN_GET_DEVICEVALUENAMES: 57 | { 58 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_131)); 59 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_131)); 60 | break; 61 | } 62 | 63 | case PLUGIN_INIT: 64 | { 65 | Plugin_131_init = true; 66 | success = true; 67 | break; 68 | } 69 | 70 | case PLUGIN_READ: 71 | { 72 | if (!Plugin_131_init) { 73 | String log = F("SHT3X : not yet initialized!"); 74 | addLog(LOG_LEVEL_ERROR, log); 75 | break; 76 | } 77 | sht30.get(); 78 | UserVar[event->BaseVarIndex] = sht30.cTemp; 79 | UserVar[event->BaseVarIndex+1] = sht30.humidity; 80 | String log = F("SHT3X: Temperature: "); 81 | log += UserVar[event->BaseVarIndex]; 82 | log += F(" Humidity: "); 83 | log += UserVar[event->BaseVarIndex + 1]; 84 | addLog(LOG_LEVEL_INFO, log); 85 | success = true; 86 | break; 87 | } 88 | } 89 | return success; 90 | } 91 | 92 | SHT3X::SHT3X(uint8_t address) 93 | { 94 | Wire.begin(); 95 | _address=address; 96 | } 97 | 98 | void SHT3X::get() 99 | { 100 | unsigned int data[6]; 101 | 102 | // Start I2C Transmission 103 | Wire.beginTransmission(_address); 104 | // Send measurement command 105 | Wire.write(0x2C); 106 | Wire.write(0x06); 107 | // Stop I2C transmission 108 | Wire.endTransmission(); 109 | delay(500); 110 | 111 | // Request 6 bytes of data 112 | Wire.requestFrom(_address, 6); 113 | 114 | // Read 6 bytes of data 115 | // cTemp msb, cTemp lsb, cTemp crc, humidity msb, humidity lsb, humidity crc 116 | if (Wire.available() == 6) 117 | { 118 | data[0] = Wire.read(); 119 | data[1] = Wire.read(); 120 | data[2] = Wire.read(); 121 | data[3] = Wire.read(); 122 | data[4] = Wire.read(); 123 | data[5] = Wire.read(); 124 | } 125 | 126 | // Convert the data 127 | cTemp = ((((data[0] * 256.0) + data[1]) * 175) / 65535.0) - 45; 128 | // fTemp = (cTemp * 1.8) + 32; 129 | humidity = ((((data[3] * 256.0) + data[4]) * 100) / 65535.0); 130 | } 131 | -------------------------------------------------------------------------------- /_P133_VL53L0X.ino: -------------------------------------------------------------------------------- 1 | #ifdef USES_P133 2 | //####################################################################################################### 3 | //########################### Plugin 133 VL53L0X I2C Ranging LIDAR ################################# 4 | //####################################################################################################### 5 | //###################################### stefan@clumsy.ch ########################################## 6 | //####################################################################################################### 7 | 8 | 9 | // needs VL53L0X library from pololu https://github.com/pololu/vl53l0x-arduino 10 | 11 | 12 | 13 | #define PLUGIN_133 14 | #define PLUGIN_ID_133 133 15 | #define PLUGIN_NAME_133 "Distance - VL53L0X [TESTING]" 16 | #define PLUGIN_VALUENAME1_133 "Distance" 17 | 18 | 19 | #include 20 | #include 21 | 22 | VL53L0X sensor; 23 | 24 | //////////////////////////// 25 | // VL53L0X Command Codes // 26 | //////////////////////////// 27 | 28 | boolean Plugin_133_init[2] = {false, false}; 29 | 30 | boolean Plugin_133(byte function, struct EventStruct *event, String& string) 31 | { 32 | boolean success = false; 33 | 34 | switch (function) 35 | { 36 | case PLUGIN_DEVICE_ADD: 37 | { 38 | Device[++deviceCount].Number = PLUGIN_ID_133; 39 | Device[deviceCount].Type = DEVICE_TYPE_I2C; 40 | Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_SINGLE; 41 | Device[deviceCount].Ports = 0; 42 | Device[deviceCount].PullUpOption = false; 43 | Device[deviceCount].InverseLogicOption = false; 44 | Device[deviceCount].FormulaOption = true; 45 | Device[deviceCount].ValueCount = 1; 46 | Device[deviceCount].SendDataOption = true; 47 | Device[deviceCount].TimerOption = true; 48 | Device[deviceCount].GlobalSyncOption = true; 49 | break; 50 | } 51 | 52 | case PLUGIN_GET_DEVICENAME: 53 | { 54 | string = F(PLUGIN_NAME_133); 55 | break; 56 | } 57 | 58 | case PLUGIN_GET_DEVICEVALUENAMES: 59 | { 60 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_133)); 61 | break; 62 | } 63 | case PLUGIN_WEBFORM_LOAD: 64 | { 65 | byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0]; 66 | int optionValues[2] = { 0x29, 0x30 }; 67 | addFormSelectorI2C(F("plugin_133_vl53l0x_i2c"), 2, optionValues, choice); 68 | addFormNote(F("SDO Low=0x29, High=0x30")); 69 | 70 | unsigned int choiceMode2 = Settings.TaskDevicePluginConfig[event->TaskIndex][1]; 71 | String optionsMode2[3]; 72 | optionsMode2[0] = F("VL53L0X_NORMAL_TIMING"); 73 | optionsMode2[1] = F("VL53L0X_FAST_TIMING"); 74 | optionsMode2[2] = F("VL53L0X_ACCURATE_TIMING"); 75 | int optionValuesMode2[3]; 76 | optionValuesMode2[0] = 80; 77 | optionValuesMode2[1] = 20; 78 | optionValuesMode2[2] = 320; 79 | addFormSelector(F("Timing"), F("plugin_133_vl53l0x_timing"), 3, optionsMode2, optionValuesMode2, choiceMode2); 80 | 81 | boolean choiceMode3 = Settings.TaskDevicePluginConfig[event->TaskIndex][2]; 82 | String optionsMode3[2]; 83 | optionsMode3[0] = F("VL53L0X_NORMAL_RANGE"); 84 | optionsMode3[1] = F("VL53L0X_LONG_RANGE"); 85 | int optionValuesMode3[2]; 86 | optionValuesMode3[0] = 0; 87 | optionValuesMode3[1] = 1; 88 | addFormSelector(F("Range"), F("plugin_133_vl53l0x_range"), 2, optionsMode3, optionValuesMode3, choiceMode3); 89 | 90 | success = true; 91 | break; 92 | } 93 | 94 | case PLUGIN_WEBFORM_SAVE: 95 | { 96 | Settings.TaskDevicePluginConfig[event->TaskIndex][0] = getFormItemInt(F("plugin_133_vl53l0x_i2c")); 97 | Settings.TaskDevicePluginConfig[event->TaskIndex][1] = getFormItemInt(F("plugin_133_vl53l0x_timing")); 98 | Settings.TaskDevicePluginConfig[event->TaskIndex][2] = getFormItemInt(F("plugin_133_vl53l0x_range")); 99 | 100 | Plugin_133_init[Settings.TaskDevicePluginConfig[event->TaskIndex][0]] = false; 101 | success = true; 102 | break; 103 | } 104 | 105 | case PLUGIN_READ: 106 | { 107 | 108 | int idx = Settings.TaskDevicePluginConfig[event->TaskIndex][0] ; 109 | // Plugin_133_init[idx] &= Plugin_133_check(Settings.TaskDevicePluginConfig[event->TaskIndex][0]); // Check id device is present 110 | 111 | // IT = Settings.TaskDevicePluginConfig[event->TaskIndex][1]; // set Integration Time 112 | 113 | if (!Plugin_133_init[idx]) 114 | { 115 | Plugin_133_init[idx] = Plugin_133_begin(Settings.TaskDevicePluginConfig[event->TaskIndex][0], Settings.TaskDevicePluginConfig[event->TaskIndex][1], Settings.TaskDevicePluginConfig[event->TaskIndex][2]); 116 | } 117 | 118 | 119 | String log = F("VL53L0X : idx: 0x"); 120 | log += String(idx, HEX); 121 | addLog(LOG_LEVEL_DEBUG, log); 122 | log = F("VL53L0X : plugin(idx): 0x"); 123 | log += String(Plugin_133_init[idx], HEX); 124 | addLog(LOG_LEVEL_DEBUG, log); 125 | 126 | if (Plugin_133_init[idx]) 127 | { 128 | success = true; 129 | long dist = sensor.readRangeSingleMillimeters(); 130 | if (sensor.timeoutOccurred()) { 131 | addLog(LOG_LEVEL_DEBUG, "VL53L0X: TIMEOUT"); 132 | success = false; 133 | } else if ( dist >= 8190 ) { 134 | addLog(LOG_LEVEL_DEBUG, "VL53L0X: NO MEASUREMENT"); 135 | success = false; 136 | } else { 137 | UserVar[event->BaseVarIndex] = dist; 138 | } 139 | 140 | addLog(LOG_LEVEL_DEBUG, log); 141 | log = F("VL53L0X: Address: 0x"); 142 | log += String(Settings.TaskDevicePluginConfig[event->TaskIndex][0], HEX); 143 | log += F(" / Timing: "); 144 | log += String(Settings.TaskDevicePluginConfig[event->TaskIndex][1], DEC); 145 | log += F(" / Long Range: "); 146 | log += String(Settings.TaskDevicePluginConfig[event->TaskIndex][2], BIN); 147 | log += F(" / Distance: "); 148 | log += UserVar[event->BaseVarIndex]; 149 | addLog(LOG_LEVEL_INFO, log); 150 | 151 | } 152 | break; 153 | } 154 | 155 | } 156 | return success; 157 | } 158 | 159 | //**************************************************************************/ 160 | // Check VL53L0X presence 161 | //**************************************************************************/ 162 | /*bool Plugin_133_check(int a) { 163 | vl53l0x_i2caddr = a ? a : 0x29; 164 | bool wire_status = false; 165 | uint16_t deviceID = Plugin_133_getVL53L0XID(a); 166 | 167 | String log = F("VL53L0X : ID: 0x"); 168 | log += String(deviceID, HEX); 169 | addLog(LOG_LEVEL_DEBUG, log); 170 | 171 | if (deviceID != 0x29) { 172 | return false; 173 | } else { 174 | return true; 175 | } 176 | } 177 | */ 178 | 179 | //**************************************************************************/ 180 | // Initialize VL53L0X 181 | //**************************************************************************/ 182 | bool Plugin_133_begin(int a, int timing, boolean range) { 183 | /* 184 | if (! Plugin_133_check(a)) 185 | return false; 186 | */ 187 | 188 | sensor.init(); 189 | sensor.setTimeout(500); 190 | 191 | if ( range ) { 192 | // lower the return signal rate limit (default is 0.25 MCPS) 193 | sensor.setSignalRateLimit(0.1); 194 | // increase laser pulse periods (defaults are 14 and 10 PCLKs) 195 | sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 18); 196 | sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 14); 197 | } 198 | 199 | sensor.setMeasurementTimingBudget(timing * 1000); 200 | 201 | delay(timing + 50); 202 | 203 | return true; 204 | } 205 | 206 | 207 | #endif 208 | -------------------------------------------------------------------------------- /_P144_RC-Switch-TX.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //#################################### Plugin 144: RC-Switch TX ######################################### 3 | //####################################################################################################### 4 | // written by Jochen Krapf (jk@nerd2nerd.org) 5 | 6 | // List of commands: 7 | // (1) RC, 8 | // (2) RC,,, 9 | 10 | // List of RC params: 11 | // (a) SEND= 12 | // Send binary code with any length ("RC,SEND=000000000001010100010001") 13 | // (b) SEND= 14 | // Send tristate code with any length ("RC,SEND=00000FFF0F0F") 15 | // (c) SENDDEC= 16 | // Send 24 bit decimal code ("RC,SENDDEC=5393") 17 | // (d) ON= 18 | // Send binary code for simple 10 DIP switch devices ("RC,ON=1010100010") 19 | // (e) ON=<1..4><1..4> 20 | // Send switch position for simple 2 rotary switch devices ("RC,ON=42") 21 | // (f) ON=<1..4><1..4> 22 | // Send switch position for Intertechno devices ("RC,ON=a42") 23 | // (f) OFF= as ON... 24 | // (g) PROTOCOL= 25 | // Set protocoln for devices ("RC,PROTOCOL=2") default=1 26 | // (h) PULSE= 27 | // Set pulse length ("RC,PULSE=320") default=320 28 | // (i) REPEAT= 29 | // Set number of transmission repeats ("RC,REPEAT=15") default=? 30 | // 31 | // Combinations: 32 | // e.g. "RC,PROTOCOL=2,PULSE=320,REPEAT=15,SEND=000000000001010100010001" 33 | 34 | 35 | #include //https://github.com/sui77/rc-switch.git 36 | 37 | static RCSwitch Plugin_144_RC = RCSwitch(); 38 | 39 | #define PLUGIN_144 40 | #define PLUGIN_ID_144 144 41 | #define PLUGIN_NAME_144 "RC-Switch TX" 42 | 43 | 44 | boolean Plugin_144(byte function, struct EventStruct *event, String& string) 45 | { 46 | boolean success = false; 47 | 48 | switch (function) 49 | { 50 | case PLUGIN_DEVICE_ADD: 51 | { 52 | Device[++deviceCount].Number = PLUGIN_ID_144; 53 | Device[deviceCount].Type = DEVICE_TYPE_SINGLE; 54 | Device[deviceCount].Ports = 0; 55 | Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_SWITCH; 56 | Device[deviceCount].PullUpOption = false; 57 | Device[deviceCount].InverseLogicOption = false; 58 | Device[deviceCount].FormulaOption = false; 59 | Device[deviceCount].ValueCount = 0; 60 | Device[deviceCount].SendDataOption = false; 61 | Device[deviceCount].TimerOption = false; 62 | Device[deviceCount].TimerOptional = false; 63 | Device[deviceCount].GlobalSyncOption = true; 64 | break; 65 | } 66 | 67 | case PLUGIN_GET_DEVICENAME: 68 | { 69 | string = F(PLUGIN_NAME_144); 70 | break; 71 | } 72 | 73 | case PLUGIN_WEBFORM_LOAD: 74 | { 75 | success = true; 76 | break; 77 | } 78 | 79 | case PLUGIN_WEBFORM_SAVE: 80 | { 81 | success = true; 82 | break; 83 | } 84 | 85 | case PLUGIN_INIT: 86 | { 87 | int pin = Settings.TaskDevicePin1[event->TaskIndex]; 88 | if (pin >= 0) 89 | { 90 | String log = F("RC-Sw: Pin "); 91 | log += pin; 92 | log += F(" "); 93 | 94 | Plugin_144_RC.enableTransmit(pin); 95 | 96 | addLog(LOG_LEVEL_INFO, log); 97 | } 98 | 99 | 100 | if (Settings.TaskDevicePin1[event->TaskIndex] >= 0) 101 | pinMode(Settings.TaskDevicePin1[event->TaskIndex], OUTPUT); 102 | if (Settings.TaskDevicePin2[event->TaskIndex] >= 0) 103 | pinMode(Settings.TaskDevicePin2[event->TaskIndex], OUTPUT); 104 | if (Settings.TaskDevicePin3[event->TaskIndex] >= 0) 105 | pinMode(Settings.TaskDevicePin3[event->TaskIndex], OUTPUT); 106 | 107 | for (byte i=0; i<3; i++) 108 | if (Settings.TaskDevicePin[i][event->TaskIndex] >= 0) 109 | pinMode(Settings.TaskDevicePin[i][event->TaskIndex], OUTPUT); 110 | 111 | success = true; 112 | break; 113 | } 114 | 115 | case PLUGIN_WRITE: 116 | { 117 | String command = parseString(string, 1); 118 | 119 | if (command == F("rc")) 120 | { 121 | String param; 122 | byte paramIdx = 2; 123 | 124 | string.replace(" ", " "); 125 | string.replace(" =", "="); 126 | string.replace("= ", "="); 127 | 128 | param = parseString(string, paramIdx++); 129 | while (param.length()) 130 | { 131 | addLog(LOG_LEVEL_DEBUG_MORE, param); 132 | 133 | int index = param.indexOf('='); 134 | if (index > 0) 135 | { 136 | String paramKey = param.substring(0, index); 137 | String paramVal = param.substring(index+1); 138 | paramKey.toUpperCase(); 139 | 140 | addLog(LOG_LEVEL_DEBUG_MORE, paramKey); 141 | addLog(LOG_LEVEL_DEBUG_MORE, paramVal); 142 | 143 | if (paramKey == F("SEND")) 144 | { 145 | if (paramVal.indexOf("F") >= 0) 146 | Plugin_144_RC.sendTriState(&(paramVal[0])); 147 | else 148 | Plugin_144_RC.send(&(paramVal[0])); 149 | } 150 | if (paramKey == F("SENDDEC")) 151 | { 152 | Plugin_144_RC.send(paramVal.toInt(), 24); 153 | } 154 | if (paramKey == F("ON")) 155 | { 156 | if (paramVal.length()==10) //simple 10 DIP switch 157 | Plugin_144_RC.switchOn(&(paramVal.substring(0, 5)[0]), &(paramVal.substring(5)[0])); 158 | else if (paramVal.length()==2) //2x rotary switch 1..4 159 | Plugin_144_RC.switchOn(paramVal[0]-'0', paramVal[1]-'0'); 160 | else if (paramVal.length()==3) //Intertechno outlets 161 | Plugin_144_RC.switchOn(paramVal[0], paramVal[1]-'0', paramVal[2]-'0'); 162 | } 163 | if (paramKey == F("OFF")) 164 | { 165 | if (paramVal.length()==10) //simple 10 DIP switch 166 | Plugin_144_RC.switchOff(&(paramVal.substring(0, 5)[0]), &(paramVal.substring(5)[0])); 167 | else if (paramVal.length()==2) //2x rotary switch 1..4 168 | Plugin_144_RC.switchOff(paramVal[0]-'0', paramVal[1]-'0'); 169 | else if (paramVal.length()==3) //Intertechno outlets 170 | Plugin_144_RC.switchOn(paramVal[0], paramVal[1]-'0', paramVal[2]-'0'); 171 | } 172 | if (paramKey == F("PROTOCOL")) 173 | { 174 | Plugin_144_RC.setProtocol(paramVal.toInt()); 175 | } 176 | if (paramKey == F("PULSE")) 177 | { 178 | Plugin_144_RC.setPulseLength(paramVal.toInt()); 179 | } 180 | if (paramKey == F("REPEAT")) 181 | { 182 | Plugin_144_RC.setRepeatTransmit(paramVal.toInt()); 183 | } 184 | } 185 | 186 | param = parseString(string, paramIdx++); 187 | } 188 | 189 | success = true; 190 | } 191 | 192 | break; 193 | } 194 | 195 | case PLUGIN_READ: 196 | { 197 | //no values 198 | success = true; 199 | break; 200 | } 201 | 202 | } 203 | return success; 204 | } 205 | -------------------------------------------------------------------------------- /_P149_MHZ19.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * This plug in is written by Dmitry (rel22 ___ inbox.ru) 4 | * Plugin is based upon SenseAir plugin by Daniel Tedenljung info__AT__tedenljungconsulting.com 5 | * 6 | * This plugin reads the CO2 and Temperateure values from MH-Z19 NDIR Sensor 7 | * DevicePin1 - is RX for ESP 8 | * DevicePin2 - is TX for ESP 9 | */ 10 | 11 | 12 | #define PLUGIN_149 13 | #define PLUGIN_ID_149 149 14 | #define PLUGIN_NAME_149 "CO2 Sensor - MH-Z19" 15 | #define PLUGIN_VALUENAME1_149 "PPM" 16 | #define PLUGIN_VALUENAME2_149 "Temperature" 17 | #define PLUGIN_READ_TIMEOUT 3000 18 | 19 | #include 20 | SoftwareSerial * Plugin_149_S8; 21 | unsigned long Plugin_149_start; 22 | boolean Plugin_149_init = false; 23 | const int Plugin_149_warmUpTime = 180000; // 3 minutes in ms 24 | 25 | // 9-bytes CMD PPM read command 26 | byte mhzCmd[9] = { 0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79 }; 27 | byte mhzResp[9]; // 9 bytes bytes response 28 | 29 | boolean Plugin_149(byte function, struct EventStruct * event, String& string) 30 | { 31 | bool success = false; 32 | 33 | switch (function) 34 | { 35 | case PLUGIN_DEVICE_ADD: 36 | { 37 | Device[++deviceCount].Number = PLUGIN_ID_149; 38 | Device[deviceCount].Type = DEVICE_TYPE_DUAL; 39 | Device[deviceCount].VType = SENSOR_TYPE_DUAL; 40 | Device[deviceCount].Ports = 0; 41 | Device[deviceCount].PullUpOption = false; 42 | Device[deviceCount].InverseLogicOption = false; 43 | Device[deviceCount].FormulaOption = true; 44 | Device[deviceCount].ValueCount = 2; 45 | Device[deviceCount].SendDataOption = true; 46 | Device[deviceCount].TimerOption = true; 47 | Device[deviceCount].GlobalSyncOption = true; 48 | break; 49 | } 50 | 51 | case PLUGIN_GET_DEVICENAME: 52 | { 53 | string = F(PLUGIN_NAME_149); 54 | break; 55 | } 56 | 57 | case PLUGIN_GET_DEVICEVALUENAMES: 58 | { 59 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_149)); 60 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_149)); 61 | break; 62 | } 63 | 64 | case PLUGIN_WEBFORM_LOAD: 65 | { 66 | addFormNote(string, F("1st GPIO connects to sensor TX pin. 2nd GPIO connects to sensor RX pin.")); 67 | success = true; 68 | break; 69 | } 70 | 71 | case PLUGIN_INIT: 72 | { 73 | if (Settings.TaskDevicePin1[event->TaskIndex] != -1 && Settings.TaskDevicePin2[event->TaskIndex] != -1) 74 | { 75 | Plugin_149_S8 = new SoftwareSerial(Settings.TaskDevicePin1[event->TaskIndex], Settings.TaskDevicePin2[event->TaskIndex]); 76 | Plugin_149_S8->begin(9600); 77 | Plugin_149_start = millis(); 78 | Plugin_149_init = false; // force warmup period 79 | String log = F("MHZ19: Init OK "); 80 | addLog(LOG_LEVEL_INFO, log); 81 | } 82 | 83 | success = true; 84 | break; 85 | } 86 | 87 | case PLUGIN_READ: 88 | { 89 | if (Plugin_149_init) 90 | { 91 | // send read PPM command 92 | int nbBytesSent = Plugin_149_S8->write(mhzCmd, 9); 93 | if (nbBytesSent != 9) 94 | { 95 | String log = F("MHZ19: Error, nb bytes sent != 9 : "); 96 | log += nbBytesSent; 97 | addLog(LOG_LEVEL_INFO, log); 98 | } 99 | 100 | // get response 101 | memset(mhzResp, 0, 9); 102 | 103 | long start = millis(); 104 | int counter = 0; 105 | while (((millis() - start) < PLUGIN_READ_TIMEOUT) && (counter < 9)) 106 | { 107 | if (Plugin_149_S8->available() > 0) 108 | { 109 | mhzResp[counter++] = Plugin_149_S8->read(); 110 | } 111 | else 112 | { 113 | delay(10); 114 | } 115 | } 116 | 117 | if (counter < 9) 118 | { 119 | String log = F("MHZ19: Error, timeout while trying to read"); 120 | addLog(LOG_LEVEL_INFO, log); 121 | } 122 | 123 | unsigned int ppm = 0; 124 | int temperature = 0; 125 | int i; 126 | byte crc = 0; 127 | for (i = 1; i < 8; i++) crc += mhzResp[i]; 128 | 129 | crc = 255 - crc; 130 | crc++; 131 | 132 | if (!(mhzResp[0] == 0xFF && mhzResp[1] == 0x86 && mhzResp[8] == crc) ) 133 | { 134 | String log = F("MHZ19: Read error : CRC = "); 135 | log += String(crc); 136 | log += " / "; 137 | log += String(mhzResp[8]); 138 | log += " bytes read => "; 139 | for (i = 0; i < 9; i++) 140 | { 141 | log += mhzResp[i]; 142 | log += "/"; 143 | } 144 | 145 | addLog(LOG_LEVEL_ERROR, log); 146 | 147 | success = false; 148 | break; 149 | } 150 | else 151 | { 152 | // calculate CO2 PPM 153 | unsigned int mhzRespHigh = (unsigned int) mhzResp[2]; 154 | unsigned int mhzRespLow = (unsigned int) mhzResp[3]; 155 | ppm = (256 * mhzRespHigh) + mhzRespLow; 156 | temperature = (unsigned int) mhzResp[4] - 40; 157 | } 158 | 159 | UserVar[event->BaseVarIndex] = (float) ppm; 160 | UserVar[event->BaseVarIndex + 1] = temperature; 161 | String log = F("MHZ19: PPM value: "); 162 | log += ppm; 163 | addLog(LOG_LEVEL_INFO, log); 164 | log = F("MH-Z19: Temperature: "); 165 | log += temperature; 166 | addLog(LOG_LEVEL_INFO, log); 167 | success = true; 168 | break; 169 | } 170 | else if (millis() - Plugin_149_start >= Plugin_149_warmUpTime) 171 | { 172 | Plugin_149_init = true; 173 | String log = F("MH-Z19 : Warmup Complete"); 174 | addLog(LOG_LEVEL_DEBUG, log); 175 | } 176 | else 177 | { 178 | // wait for warmup 179 | String log = F("MH-Z19 : warming up (seconds) : "); 180 | log += ((millis() - Plugin_149_start) / 1000); 181 | log += F(" of "); 182 | log += Plugin_149_warmUpTime/1000; 183 | addLog(LOG_LEVEL_DEBUG, log); 184 | } 185 | break; 186 | } 187 | } 188 | 189 | return success; 190 | } // Plugin_149 191 | -------------------------------------------------------------------------------- /_P150_SDM120C.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //############################# Plugin 150: SDM120C Eastron Energy Meter ################################ 3 | //####################################################################################################### 4 | /* 5 | Plugin written by: Sergio Faustino sjfaustino__AT__gmail.com 6 | 7 | This plugin reads available values of an Eastron SDM120C Energy Meter. 8 | It will also work with all the other superior model such as SDM220 AND SDM630 series. 9 | */ 10 | 11 | #ifdef PLUGIN_BUILD_DEV 12 | 13 | #define PLUGIN_150 14 | #define PLUGIN_ID_150 150 15 | #define PLUGIN_NAME_150 "Energy (AC) - Eastron SDM120C" 16 | #define PLUGIN_VALUENAME1_150 "Voltage" 17 | 18 | boolean Plugin_150_init = false; 19 | 20 | #include // Requires SDM library from Reaper7 - https://github.com/reaper7/SDM_Energy_Meter/ 21 | SDM<2400, D6, D7> Plugin_150_SDM; 22 | 23 | boolean Plugin_150(byte function, struct EventStruct *event, String& string) 24 | { 25 | boolean success = false; 26 | 27 | switch (function) 28 | { 29 | 30 | case PLUGIN_DEVICE_ADD: 31 | { 32 | Device[++deviceCount].Number = PLUGIN_ID_150; 33 | Device[deviceCount].Type = DEVICE_TYPE_DUAL; // connected through 2 datapins 34 | Device[deviceCount].VType = SENSOR_TYPE_SINGLE; 35 | Device[deviceCount].Ports = 0; 36 | Device[deviceCount].PullUpOption = false; 37 | Device[deviceCount].InverseLogicOption = false; 38 | Device[deviceCount].FormulaOption = true; 39 | Device[deviceCount].ValueCount = 1; 40 | Device[deviceCount].SendDataOption = true; 41 | Device[deviceCount].TimerOption = true; 42 | Device[deviceCount].GlobalSyncOption = true; 43 | break; 44 | } 45 | 46 | case PLUGIN_GET_DEVICENAME: 47 | { 48 | string = F(PLUGIN_NAME_150); 49 | break; 50 | } 51 | 52 | case PLUGIN_GET_DEVICEVALUENAMES: 53 | { 54 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_150)); 55 | break; 56 | } 57 | 58 | case PLUGIN_WEBFORM_LOAD: 59 | { 60 | byte meter_model = Settings.TaskDevicePluginConfig[event->TaskIndex][1]; 61 | byte meter_baudrate = Settings.TaskDevicePluginConfig[event->TaskIndex][2]; 62 | byte query = Settings.TaskDevicePluginConfig[event->TaskIndex][3]; 63 | 64 | String options_model[3] = { F("SDM120C"), F("SDM220T"), F("SDM630") }; 65 | addFormSelector(string, F("Model Type"), F("plugin_150_meter_model"), 3, options_model, NULL, meter_model ); 66 | 67 | String options_baudrate[6] = { F("1200"), F("2400"), F("4800"), F("9600"), F("19200"), F("38400") }; 68 | addFormSelector(string, F("Baud Rate"), F("plugin_150_meter_baudrate"), 6, options_baudrate, NULL, meter_baudrate ); 69 | 70 | if (meter_model == 0 && meter_baudrate > 3) 71 | string += F(" SDM120 only allows up to 9600 baud with default 2400!"); 72 | 73 | if (meter_model == 2 && meter_baudrate == 0) 74 | string += F(" SDM630 only allows 2400 to 38400 baud with default 9600!"); 75 | 76 | String options_query[10] = { F("Voltage (V)"), 77 | F("Current (A)"), 78 | F("Power (W)"), 79 | F("Active Apparent Power (VA)"), 80 | F("Reactive Apparent Power (VAr)"), 81 | F("Power Factor (cfi?)"), 82 | F("Frequency (Hz)"), 83 | F("Import Active Energy (Wh)"), 84 | F("Export Active Energy (Wh)"), 85 | F("Total Active Energy (Wh)") }; 86 | addFormSelector(string, F("Query"), F("plugin_150_query"), 10, options_query, NULL, query ); 87 | 88 | success = true; 89 | break; 90 | } 91 | 92 | case PLUGIN_WEBFORM_SAVE: 93 | { 94 | Settings.TaskDevicePluginConfig[event->TaskIndex][0] = getFormItemInt(F("plugin_150")); 95 | Settings.TaskDevicePluginConfig[event->TaskIndex][1] = getFormItemInt(F("plugin_150_meter_model")); 96 | Settings.TaskDevicePluginConfig[event->TaskIndex][2] = getFormItemInt(F("plugin_150_meter_baudrate")); 97 | Settings.TaskDevicePluginConfig[event->TaskIndex][3] = getFormItemInt(F("plugin_150_query")); 98 | 99 | Plugin_150_init = false; // Force device setup next time 100 | success = true; 101 | break; 102 | } 103 | 104 | case PLUGIN_INIT: 105 | { 106 | Plugin_150_init = true; 107 | 108 | // SDM<2400, Settings.TaskDevicePin1[event->TaskIndex], 109 | // Settings.TaskDevicePin2[event->TaskIndex]> Plugin_150_SDM; 110 | Plugin_150_SDM.begin(); 111 | success = true; 112 | break; 113 | } 114 | 115 | case PLUGIN_READ: 116 | { 117 | 118 | if (Plugin_150_init) 119 | { 120 | float _tempvar = 0; 121 | String log = F("EASTRON: "); 122 | switch(Settings.TaskDevicePluginConfig[event->TaskIndex][3]) 123 | { 124 | case 0: 125 | { 126 | _tempvar = Plugin_150_SDM.readVal(SDM120C_VOLTAGE); 127 | log += F("Voltage "); 128 | break; 129 | } 130 | case 1: 131 | { 132 | _tempvar = Plugin_150_SDM.readVal(SDM120C_CURRENT); 133 | log += F("Current "); 134 | break; 135 | } 136 | case 2: 137 | { 138 | _tempvar = Plugin_150_SDM.readVal(SDM120C_POWER); 139 | log += F("Power "); 140 | break; 141 | } 142 | case 3: 143 | { 144 | _tempvar = Plugin_150_SDM.readVal(SDM120C_ACTIVE_APPARENT_POWER); 145 | log += F("Active Apparent Power "); 146 | break; 147 | } 148 | case 4: 149 | { 150 | _tempvar = Plugin_150_SDM.readVal(SDM120C_REACTIVE_APPARENT_POWER); 151 | log += F("Reactive Apparent Power "); 152 | break; 153 | } 154 | case 5: 155 | { 156 | _tempvar = Plugin_150_SDM.readVal(SDM120C_POWER_FACTOR); 157 | log += F("Power Factor "); 158 | break; 159 | } 160 | case 6: 161 | { 162 | _tempvar = Plugin_150_SDM.readVal(SDM120C_FREQUENCY); 163 | log += F("Frequency "); 164 | break; 165 | } 166 | case 7: 167 | { 168 | _tempvar = Plugin_150_SDM.readVal(SDM120C_IMPORT_ACTIVE_ENERGY); 169 | log += F("Import Active Energy "); 170 | break; 171 | } 172 | case 8: 173 | { 174 | _tempvar = Plugin_150_SDM.readVal(SDM120C_EXPORT_ACTIVE_ENERGY); 175 | log += F("Export Active Energy "); 176 | break; 177 | } 178 | case 9: 179 | { 180 | _tempvar = Plugin_150_SDM.readVal(SDM120C_TOTAL_ACTIVE_ENERGY); 181 | log += F("Total Active Energy "); 182 | break; 183 | } 184 | } 185 | 186 | UserVar[event->BaseVarIndex] = _tempvar; 187 | log += _tempvar; 188 | addLog(LOG_LEVEL_INFO, log); 189 | 190 | success = true; 191 | break; 192 | } 193 | break; 194 | } 195 | } 196 | return success; 197 | } 198 | 199 | #endif 200 | -------------------------------------------------------------------------------- /_P152_MCP42010.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //#################################### Plugin 152: MCP42010 ############################################# 3 | //################################## I use GPIO 12 / 14 / 15 ############################################ 4 | //####################################################################################################### 5 | // written by antibill 6 | 7 | // Usage: 8 | // (1): Set value to potentiometer (http://xx.xx.xx.xx/control?cmd=digipot,0,255) 9 | // (2): Set value to potentiometer (http://xx.xx.xx.xx/control?cmd=digipot,1,0) 10 | 11 | #include 12 | // https://github.com/mensink/arduino-lib-MCP42010 13 | static float Plugin_152_PotDest[2] = {0,0}; 14 | 15 | #define PLUGIN_152 16 | #define PLUGIN_ID_152 152 17 | #define PLUGIN_NAME_152 "MCP42010" 18 | #define PLUGIN_VALUENAME1_152 "DigiPot0" 19 | #define PLUGIN_VALUENAME2_152 "DigiPot1" 20 | 21 | 22 | 23 | boolean Plugin_152(byte function, struct EventStruct *event, String& string) 24 | { 25 | boolean success = false; 26 | 27 | switch (function) 28 | { 29 | case PLUGIN_DEVICE_ADD: 30 | { 31 | Device[++deviceCount].Number = PLUGIN_ID_152; 32 | Device[deviceCount].Type = DEVICE_TYPE_DUMMY; // SPI pins for ESP8266 are CS=15, CLK=14, MOSI=13 33 | Device[deviceCount].Ports = 0; 34 | Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_DUAL; 35 | Device[deviceCount].PullUpOption = false; 36 | Device[deviceCount].InverseLogicOption = false; 37 | Device[deviceCount].FormulaOption = true; 38 | Device[deviceCount].ValueCount = 2; 39 | Device[deviceCount].SendDataOption = true; 40 | Device[deviceCount].TimerOption = true; 41 | Device[deviceCount].TimerOptional = true; 42 | Device[deviceCount].GlobalSyncOption = true; 43 | break; 44 | } 45 | 46 | case PLUGIN_GET_DEVICENAME: 47 | { 48 | string = F(PLUGIN_NAME_152); 49 | break; 50 | } 51 | 52 | case PLUGIN_GET_DEVICEVALUENAMES: 53 | { 54 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_152)); 55 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_152)); 56 | break; 57 | } 58 | 59 | case PLUGIN_GET_DEVICEGPIONAMES: 60 | { 61 | event->String1 = formatGpioName_bidirectional(F("CS")); 62 | event->String2 = formatGpioName_bidirectional(F("CLK")); 63 | event->String3 = formatGpioName_bidirectional(F("MOSI")); 64 | break; 65 | } 66 | 67 | case PLUGIN_WEBFORM_LOAD: 68 | { 69 | success = true; 70 | break; 71 | } 72 | 73 | case PLUGIN_WEBFORM_SAVE: 74 | { 75 | success = true; 76 | break; 77 | } 78 | 79 | case PLUGIN_INIT: 80 | { 81 | success = true; 82 | break; 83 | } 84 | 85 | case PLUGIN_WRITE: 86 | { 87 | 88 | String tmpString = string; 89 | int argIndex = tmpString.indexOf(','); 90 | if (argIndex) 91 | tmpString = tmpString.substring(0, argIndex); 92 | 93 | if (tmpString.equalsIgnoreCase(F("digipot"))) 94 | { 95 | int pot; 96 | int value; 97 | pot = event->Par1; 98 | value = event->Par2; 99 | 100 | MCP42010 digipot(Settings.TaskDevicePin1[event->TaskIndex], 101 | Settings.TaskDevicePin2[event->TaskIndex], 102 | Settings.TaskDevicePin3[event->TaskIndex]); 103 | Plugin_152_PotDest[pot] = value; 104 | digipot.setPot(pot,value); 105 | success = true; 106 | } 107 | 108 | 109 | break; 110 | } 111 | 112 | case PLUGIN_READ: 113 | { 114 | 115 | UserVar[event->BaseVarIndex + 0]= Plugin_152_PotDest[0] ; 116 | UserVar[event->BaseVarIndex + 1] = Plugin_152_PotDest[1] ; 117 | success = true; 118 | break; 119 | } 120 | 121 | 122 | } 123 | return success; 124 | } 125 | -------------------------------------------------------------------------------- /_P153_MAX44009.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //############################### Plugin 153: MAX44009 I2C 0x4A ####################################### 3 | //####################################################################################################### 4 | 5 | // based on : 6 | // 1) https://github.com/RobTillaart/Arduino/tree/master/libraries/Max44009 7 | // 2) https://github.com/dantudose/MAX44009/blob/master/MAX44009.cpp 8 | // 9 | // written by https://github.com/apszowski 10 | 11 | #ifdef PLUGIN_BUILD_TESTING 12 | 13 | #define PLUGIN_153 14 | #define PLUGIN_ID_153 153 15 | #define PLUGIN_NAME_153 "Light/Lux - MAX44009 (GY-49)" 16 | #define PLUGIN_VALUENAME1_153 "Lux" 17 | 18 | boolean Plugin_153_init = false; 19 | 20 | enum { 21 | //I2C address 22 | MAX44009_I2C_ADDR = 0x4A, 23 | // CONFIGURATION 24 | MAX44009_REGISTER_CONFIGURATION = 0x02, 25 | MAX44009_CFG_MANUAL = 0x40, 26 | MAX44009_CFG_CONTINUOUS = 0x80, 27 | //LUX READING 28 | MAX44009_REGISTER_LUX_HIGH = 0x03, 29 | MAX44009_REGISTER_LUX_LOW = 0x04, 30 | }; 31 | 32 | 33 | uint16_t Plugin_153_readRegister(uint8_t reg) { 34 | uint16_t ret; 35 | Wire.beginTransmission(MAX44009_I2C_ADDR); 36 | Wire.write(reg); 37 | Wire.endTransmission(); 38 | Wire.requestFrom(MAX44009_I2C_ADDR,1); 39 | ret = Wire.read(); 40 | return ret; 41 | } 42 | 43 | void Plugin_153_writeRegister(uint8_t reg, uint8_t value){ 44 | Wire.beginTransmission(MAX44009_I2C_ADDR); 45 | Wire.write(reg); 46 | Wire.write(value); 47 | Wire.endTransmission(); 48 | } 49 | 50 | float Plugin_153_readLux(void) 51 | { 52 | uint8_t luxHigh = Plugin_153_readRegister(MAX44009_REGISTER_LUX_HIGH); 53 | uint8_t luxLow = Plugin_153_readRegister(MAX44009_REGISTER_LUX_LOW); 54 | uint8_t e = (luxHigh & 0xF0) >> 4; 55 | uint8_t m = (luxHigh & 0x0F) << 4 | luxLow; 56 | float lux = pow(2,e) * m * 0.045; 57 | return lux; 58 | } 59 | void Plugin_153_setModeAutomatic(void) 60 | { 61 | uint8_t config = Plugin_153_readRegister(MAX44009_REGISTER_CONFIGURATION); 62 | config &= ~MAX44009_CFG_CONTINUOUS; // off 63 | config &= ~MAX44009_CFG_MANUAL; // off 64 | Plugin_153_writeRegister(MAX44009_REGISTER_CONFIGURATION, config); 65 | } 66 | 67 | void Plugin_153_setModeContinuous(void) 68 | { 69 | uint8_t config = Plugin_153_readRegister(MAX44009_REGISTER_CONFIGURATION); 70 | config |= MAX44009_CFG_CONTINUOUS; // on 71 | config &= ~MAX44009_CFG_MANUAL; // off 72 | Plugin_153_writeRegister(MAX44009_REGISTER_CONFIGURATION, config); 73 | } 74 | 75 | void Plugin_153_setModeManual(uint8_t CDR, uint8_t TIM) 76 | { 77 | uint8_t config = Plugin_153_readRegister(MAX44009_REGISTER_CONFIGURATION); 78 | config &= ~MAX44009_CFG_CONTINUOUS; // off 79 | config |= MAX44009_CFG_MANUAL; // on 80 | config &= 0xF0; // clear CDR & TIM bits 81 | config |= CDR << 3 | TIM; 82 | Plugin_153_writeRegister(MAX44009_REGISTER_CONFIGURATION, config); 83 | } 84 | 85 | boolean Plugin_153(byte function, struct EventStruct *event, String& string) 86 | { 87 | boolean success = false; 88 | switch (function) 89 | { 90 | case PLUGIN_DEVICE_ADD: 91 | { 92 | Device[++deviceCount].Number = PLUGIN_ID_153; 93 | Device[deviceCount].Type = DEVICE_TYPE_I2C; 94 | Device[deviceCount].VType = SENSOR_TYPE_SINGLE; 95 | Device[deviceCount].Ports = 0; 96 | Device[deviceCount].PullUpOption = false; 97 | Device[deviceCount].InverseLogicOption = false; 98 | Device[deviceCount].FormulaOption = true; 99 | Device[deviceCount].SendDataOption = true; 100 | Device[deviceCount].ValueCount = 1; 101 | Device[deviceCount].TimerOption = true; 102 | Device[deviceCount].GlobalSyncOption = true; 103 | break; 104 | } 105 | 106 | case PLUGIN_GET_DEVICENAME: 107 | { 108 | string = F(PLUGIN_NAME_153); 109 | break; 110 | } 111 | 112 | case PLUGIN_GET_DEVICEVALUENAMES: 113 | { 114 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_153)); 115 | break; 116 | } 117 | 118 | case PLUGIN_WEBFORM_LOAD: 119 | { 120 | byte choice_mode = Settings.TaskDevicePluginConfig[event->TaskIndex][0]; 121 | String options[8]; 122 | int optionValues[8]; 123 | options[0] = F("Automatic"); 124 | optionValues[0] = 0; 125 | options[1] = F("Continuous"); 126 | optionValues[1] = 1; 127 | options[2] = F("Manual"); 128 | optionValues[2] = 2; 129 | addFormSelector(string, F("Mode"), F("plugin_153_mode"), 3, options, optionValues, choice_mode); 130 | if( choice_mode != 0) 131 | { 132 | //######################## 133 | byte choice_division = Settings.TaskDevicePluginConfig[event->TaskIndex][1]; 134 | options[0] = F("Not divided"); 135 | optionValues[0] = 0; 136 | options[1] = F("Divided 1/8"); 137 | optionValues[1] = 1; 138 | addFormSelector(string, F("Current Division Ratio"), F("plugin_153_divide"), 2, options, optionValues, choice_division); 139 | //######################## 140 | byte choice_time = Settings.TaskDevicePluginConfig[event->TaskIndex][2]; 141 | float integration_time = 800; 142 | for( uint8_t i = 0 ; i <= 7 ; i++) 143 | { 144 | options[i] = integration_time; 145 | options[i] += F(" ms"); 146 | optionValues[i] = i; 147 | integration_time = integration_time/2.0; 148 | } 149 | addFormSelector(string, F("Integration Time"), F("plugin_153_time"), 8, options, optionValues, choice_time); 150 | } 151 | success = true; 152 | break; 153 | } 154 | 155 | case PLUGIN_WEBFORM_SAVE: 156 | { 157 | Settings.TaskDevicePluginConfig[event->TaskIndex][0] = getFormItemInt(F("plugin_153_mode")); 158 | Settings.TaskDevicePluginConfig[event->TaskIndex][1] = getFormItemInt(F("plugin_153_divide")); 159 | Settings.TaskDevicePluginConfig[event->TaskIndex][2] = getFormItemInt(F("plugin_153_time")); 160 | Plugin_153_init = false; 161 | success = true; 162 | break; 163 | } 164 | 165 | case PLUGIN_INIT: 166 | { 167 | Plugin_153_init = true; 168 | uint8_t mode = Settings.TaskDevicePluginConfig[event->TaskIndex][0]; 169 | uint8_t divide = Settings.TaskDevicePluginConfig[event->TaskIndex][1]; 170 | uint8_t tim = Settings.TaskDevicePluginConfig[event->TaskIndex][2]; 171 | 172 | String log = F("MAX44009 :\rINIT MODE = "); 173 | if( mode == 0) 174 | { 175 | Plugin_153_setModeAutomatic(); 176 | log += F("Automatic"); 177 | } 178 | else if ( mode == 1) 179 | { 180 | Plugin_153_setModeContinuous(); 181 | log += F("Continuous"); 182 | } 183 | else 184 | { 185 | Plugin_153_setModeManual(divide,tim); 186 | log += F("Manual , CDR = "); 187 | log += divide; 188 | log += F(", Integration Time = "); 189 | log += tim; 190 | } 191 | addLog(LOG_LEVEL_INFO,log); 192 | success = true; 193 | break; 194 | } 195 | 196 | case PLUGIN_READ: 197 | { 198 | UserVar[event->BaseVarIndex] = (float) Plugin_153_readLux(); 199 | String log = F("MAX44009 : Ambient Light: "); 200 | log += UserVar[event->BaseVarIndex]; 201 | addLog(LOG_LEVEL_INFO,log); 202 | success = true; 203 | break; 204 | } 205 | } 206 | return success; 207 | } 208 | 209 | #endif 210 | -------------------------------------------------------------------------------- /_P171_PZEM-004T.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //################### Plugin 171 PZEM-004T AC Current and Voltage measurement sensor #################### 3 | //####################################################################################################### 4 | // 5 | // This plugin is interfacing with PZEM-004T Sesor with softserial communication as the sensor 6 | // has an UART pinout (TX/RX/VCC/GND) 7 | // 8 | 9 | //#ifdef PLUGIN_BUILD_TESTING 10 | 11 | #include 12 | #include 13 | PZEM004T *Plugin_171_pzem; 14 | IPAddress pzemIP(192,168,1,1); // required by the library but not used (dummy value) 15 | 16 | #define PLUGIN_171 17 | #define PLUGIN_ID_171 171 18 | #define PLUGIN_171_DEBUG false //activate extra log info in the debug 19 | #define PLUGIN_NAME_171 "Voltage & Current (AC) - PZEM-004T [TESTING]" 20 | #define PLUGIN_VALUENAME1_171 "Voltage (V)" 21 | #define PLUGIN_VALUENAME2_171 "Current (A)" 22 | #define PLUGIN_VALUENAME3_171 "Power (W)" 23 | #define PLUGIN_VALUENAME4_171 "Energy (Wh)" 24 | 25 | // local parameter for this plugin 26 | #define PZEM_MAX_ATTEMPT 3 27 | 28 | boolean Plugin_171(byte function, struct EventStruct *event, String& string) 29 | { 30 | boolean success = false; 31 | 32 | switch (function) 33 | { 34 | case PLUGIN_DEVICE_ADD: 35 | { 36 | Device[++deviceCount].Number = PLUGIN_ID_171; 37 | Device[deviceCount].Type = DEVICE_TYPE_DUAL; 38 | Device[deviceCount].VType = SENSOR_TYPE_QUAD; 39 | Device[deviceCount].Ports = 0; 40 | Device[deviceCount].PullUpOption = false; 41 | Device[deviceCount].InverseLogicOption = false; 42 | Device[deviceCount].FormulaOption = true; 43 | Device[deviceCount].ValueCount = 4; 44 | Device[deviceCount].SendDataOption = true; 45 | Device[deviceCount].TimerOption = true; 46 | Device[deviceCount].GlobalSyncOption = false; 47 | break; 48 | } 49 | 50 | case PLUGIN_GET_DEVICENAME: 51 | { 52 | string = F(PLUGIN_NAME_171); 53 | break; 54 | } 55 | 56 | case PLUGIN_GET_DEVICEVALUENAMES: 57 | { 58 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_171)); 59 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_171)); 60 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_171)); 61 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[3], PSTR(PLUGIN_VALUENAME4_171)); 62 | break; 63 | } 64 | 65 | case PLUGIN_WEBFORM_LOAD: 66 | { 67 | addFormNote(string, F("SoftSerial: 1st=RX-Pin, 2nd=TX-Pin")); 68 | success = true; 69 | break; 70 | } 71 | 72 | case PLUGIN_WEBFORM_SAVE: 73 | { 74 | success = true; 75 | break; 76 | } 77 | 78 | case PLUGIN_READ: 79 | { 80 | if (PLUGIN_171_DEBUG) { 81 | String log = F("PZEM004T: Reading started."); 82 | addLog(LOG_LEVEL_INFO, log); 83 | } 84 | float pzVoltage = Plugin171_ReadVoltage(); 85 | float pzCurrent = Plugin171_ReadCurrent(); 86 | float pzPower = Plugin171_ReadPower(); 87 | float pzEnergy = Plugin171_ReadEnergy(); 88 | //------------------------------------------------------------------- 89 | // readings can be ZERO if there's no AC input on the module. 90 | // in this case V A and W are reported correctly as ZERO but 91 | // the accumulated Energy paramenter will not be saved so to 92 | // preserve previous value 93 | //------------------------------------------------------------------- 94 | UserVar[event->BaseVarIndex] = pzVoltage; 95 | UserVar[event->BaseVarIndex + 1] = pzCurrent; 96 | UserVar[event->BaseVarIndex + 2] = pzPower; 97 | if (pzEnergy>=0) UserVar[event->BaseVarIndex + 3] = pzEnergy; 98 | if (PLUGIN_171_DEBUG) { 99 | String log = F("PZEM004T: Reading completed."); 100 | addLog(LOG_LEVEL_INFO, log); 101 | } 102 | success = true; 103 | break; 104 | } 105 | 106 | case PLUGIN_INIT: 107 | { 108 | if (!Plugin_171_pzem) 109 | { 110 | int pzemRXpin = Settings.TaskDevicePin1[event->TaskIndex]; 111 | int pzemTXpin = Settings.TaskDevicePin2[event->TaskIndex]; 112 | Plugin_171_pzem = new PZEM004T(pzemRXpin, pzemTXpin); 113 | if (PLUGIN_171_DEBUG) { 114 | String log = F("PZEM004T: Object Initialized"); 115 | log += F(" - RX-Pin="); log += pzemRXpin; 116 | log += F(" - TX-Pin="); log += pzemTXpin; 117 | addLog(LOG_LEVEL_INFO, log); 118 | } 119 | Plugin_171_pzem->setAddress(pzemIP); // This initializes the PZEM004T library using a (useless) fake IP address 120 | if (PLUGIN_171_DEBUG) { 121 | String log = F("PZEM004T: setup address (dummy)"); 122 | log += F(" - "); log += pzemIP; 123 | addLog(LOG_LEVEL_INFO, log); 124 | } 125 | } 126 | success = true; 127 | break; 128 | } 129 | 130 | } 131 | return success; 132 | } 133 | 134 | //************************************// 135 | //***** reading values functions *****// 136 | //************************************// 137 | 138 | // NOTE: readings are attempted only PZEM_AMX_ATTEMPT times 139 | 140 | float Plugin171_ReadVoltage() { 141 | int counter = 0; 142 | float reading = -1.0; 143 | do { 144 | reading = Plugin_171_pzem->voltage(pzemIP); 145 | wdt_reset(); 146 | counter++; 147 | } while (counter < PZEM_MAX_ATTEMPT && reading < 0.0); 148 | if (reading == -1) reading = 0; 149 | return reading; 150 | } 151 | 152 | float Plugin171_ReadCurrent() { 153 | int counter = 0; 154 | float reading = -1.0; 155 | do { 156 | reading = Plugin_171_pzem->current(pzemIP); 157 | wdt_reset(); 158 | counter++; 159 | } while (counter < PZEM_MAX_ATTEMPT && reading < 0.0); 160 | if (reading == -1) reading = 0; 161 | return reading; 162 | } 163 | 164 | float Plugin171_ReadPower() { 165 | int counter = 0; 166 | float reading = -1.0; 167 | do { 168 | reading = Plugin_171_pzem->power(pzemIP); 169 | wdt_reset(); 170 | counter++; 171 | } while (counter < PZEM_MAX_ATTEMPT && reading < 0.0); 172 | if (reading == -1) reading = 0; 173 | return reading; 174 | } 175 | 176 | float Plugin171_ReadEnergy() { 177 | int counter = 0; 178 | float reading = -1.0; 179 | do { 180 | reading = Plugin_171_pzem->energy(pzemIP); 181 | wdt_reset(); 182 | counter++; 183 | } while (counter < PZEM_MAX_ATTEMPT && reading < 0.0); 184 | return reading; 185 | } 186 | //#endif 187 | -------------------------------------------------------------------------------- /_P178_SCD30.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //############ Plugin 178 SCD30 I2C CO2, Humidity and Temperature Sensor ################################ 3 | //####################################################################################################### 4 | // development version 5 | // by: V0JT4 6 | // this plugin is based on the Frogmore42 library 7 | // written based code from https://github.com/Frogmore42/Sonoff-Tasmota/tree/development/lib/FrogmoreScd30 8 | // Commands: 9 | // SCDGETABC - shows automatic calibration period in days, 0 = disable 10 | // SCDGETALT - shows altitude compensation configuration in meters above sea level 11 | // SCDGETTMP - hows temperature offset in C 12 | 13 | #define PLUGIN_178 14 | #define PLUGIN_ID_178 178 15 | #define PLUGIN_NAME_178 "Gases - CO2 SCD30" 16 | #define PLUGIN_VALUENAME1_178 "CO2" 17 | #define PLUGIN_VALUENAME2_178 "Humidity" 18 | #define PLUGIN_VALUENAME3_178 "Temperature" 19 | #define PLUGIN_VALUENAME4_178 "CO2raw" 20 | 21 | #include "FrogmoreScd30.h" 22 | boolean Plugin_178_init = false; 23 | FrogmoreScd30 scd30; 24 | 25 | boolean plugin_178_begin() 26 | { 27 | if (!Plugin_178_init) 28 | { 29 | //Wire.begin(); called in ESPEasy framework 30 | scd30.begin(); 31 | uint16_t calibration = 0; 32 | scd30.getCalibrationType(&calibration); 33 | if (calibration) 34 | { 35 | scd30.setManualCalibration(); 36 | } 37 | scd30.beginMeasuring(); 38 | Plugin_178_init = true; 39 | } 40 | return(true); 41 | } 42 | 43 | boolean Plugin_178(byte function, struct EventStruct *event, String& string) 44 | { 45 | boolean success = false; 46 | 47 | switch (function) 48 | { 49 | case PLUGIN_DEVICE_ADD: 50 | { 51 | Device[++deviceCount].Number = PLUGIN_ID_178; 52 | Device[deviceCount].Type = DEVICE_TYPE_I2C; 53 | Device[deviceCount].VType = SENSOR_TYPE_QUAD; 54 | Device[deviceCount].Ports = 0; 55 | Device[deviceCount].PullUpOption = false; 56 | Device[deviceCount].InverseLogicOption = false; 57 | Device[deviceCount].FormulaOption = true; 58 | Device[deviceCount].ValueCount = 4; 59 | Device[deviceCount].SendDataOption = true; 60 | Device[deviceCount].TimerOption = true; 61 | Device[deviceCount].GlobalSyncOption = true; 62 | break; 63 | } 64 | 65 | case PLUGIN_GET_DEVICENAME: 66 | { 67 | string = F(PLUGIN_NAME_178); 68 | break; 69 | } 70 | 71 | case PLUGIN_GET_DEVICEVALUENAMES: 72 | { 73 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_178)); 74 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_178)); 75 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_178)); 76 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[3], PSTR(PLUGIN_VALUENAME4_178)); 77 | break; 78 | } 79 | 80 | case PLUGIN_WEBFORM_LOAD: 81 | { 82 | addFormNumericBox(F("Altitude"), F("plugin_178_SCD30_alt"), Settings.TaskDevicePluginConfig[event->TaskIndex][0]); 83 | addUnit(F("m")); 84 | addFormTextBox(F("Temp offset"), F("plugin_178_SCD30_tmp"), String(PCONFIG_FLOAT(0), 2), 5); 85 | addUnit(F("C")); 86 | addHtml(F("Tools->Advanced->I2C ClockStretchLimit should be set to 20000")); 87 | success = true; 88 | break; 89 | } 90 | 91 | case PLUGIN_WEBFORM_SAVE: 92 | { 93 | uint16_t alt = getFormItemInt(F("plugin_178_SCD30_alt")); 94 | if (alt > 2000) alt = 2000; 95 | Settings.TaskDevicePluginConfig[event->TaskIndex][0] = alt; 96 | PCONFIG_FLOAT(0) = getFormItemFloat(F("plugin_178_SCD30_tmp")); 97 | success = true; 98 | break; 99 | } 100 | case PLUGIN_INIT: 101 | { 102 | plugin_178_begin(); 103 | scd30.setAltitudeCompensation(Settings.TaskDevicePluginConfig[event->TaskIndex][0]); 104 | scd30.setTemperatureOffset(PCONFIG_FLOAT(0)); 105 | break; 106 | } 107 | case PLUGIN_READ: 108 | { 109 | plugin_178_begin(); 110 | uint16_t scd30_CO2 = 0; 111 | uint16_t scd30_CO2EAvg = 0; 112 | float scd30_Humid = 0.0; 113 | float scd30_Temp = 0.0; 114 | switch (scd30.readMeasurement(&scd30_CO2, &scd30_CO2EAvg, &scd30_Temp, &scd30_Humid)) 115 | { 116 | case ERROR_SCD30_NO_ERROR: 117 | UserVar[event->BaseVarIndex] = scd30_CO2EAvg; 118 | UserVar[event->BaseVarIndex+1] = scd30_Humid; 119 | UserVar[event->BaseVarIndex+2] = scd30_Temp; 120 | UserVar[event->BaseVarIndex+3] = scd30_CO2; 121 | if (scd30_CO2EAvg > 5000) 122 | { 123 | addLog(LOG_LEVEL_INFO,F("SCD30: Sensor saturated! > 5000 ppm")); 124 | } 125 | break; 126 | case ERROR_SCD30_NO_DATA: 127 | case ERROR_SCD30_CRC_ERROR: 128 | case ERROR_SCD30_CO2_ZERO: 129 | break; 130 | default: 131 | { 132 | scd30.softReset(); 133 | } 134 | break; 135 | } 136 | success = true; 137 | break; 138 | } 139 | case PLUGIN_WRITE: 140 | { 141 | uint16_t value = 0; 142 | String log = F(""); 143 | float temp; 144 | if (string.equalsIgnoreCase(F("SCDGETABC"))) 145 | { 146 | scd30.getCalibrationType(&value); 147 | log += F("ABC: "); 148 | log += value; 149 | } else if (string.equalsIgnoreCase(F("SCDGETALT"))) 150 | { 151 | scd30.getAltitudeCompensation(&value); 152 | log += F("Altitude: "); 153 | log += value; 154 | } else if (string.equalsIgnoreCase(F("SCDGETTMP"))) 155 | { 156 | scd30.getTemperatureOffset(&temp); 157 | log += F("Temp offset: "); 158 | log += String(temp,2); 159 | } else 160 | { 161 | break; 162 | } 163 | SendStatus(event->Source, log); 164 | success = true; 165 | break; 166 | } 167 | } 168 | return success; 169 | } 170 | -------------------------------------------------------------------------------- /_P180_Mux.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //#################################### Plugin 180: Analog ############################################### 3 | //####################################################################################################### 4 | 5 | #define PLUGIN_180 6 | #define PLUGIN_ID_180 180 7 | #define PLUGIN_NAME_180 "Mux Analog input - 74151/74152/74153 [TESTING]" 8 | #define PLUGIN_VALUENAME1_180 "Analog" 9 | boolean Plugin_180(byte function, struct EventStruct *event, String& string) 10 | { 11 | boolean success = false; 12 | 13 | switch (function) 14 | { 15 | 16 | case PLUGIN_DEVICE_ADD: 17 | { 18 | Device[++deviceCount].Number = PLUGIN_ID_180; 19 | Device[deviceCount].Type = DEVICE_TYPE_TRIPLE; 20 | Device[deviceCount].VType = SENSOR_TYPE_SINGLE; 21 | Device[deviceCount].Ports = 8; 22 | Device[deviceCount].PullUpOption = false; 23 | Device[deviceCount].InverseLogicOption = false; 24 | Device[deviceCount].FormulaOption = true; 25 | Device[deviceCount].ValueCount = 1; 26 | Device[deviceCount].SendDataOption = true; 27 | Device[deviceCount].TimerOption = true; 28 | Device[deviceCount].GlobalSyncOption = true; 29 | break; 30 | } 31 | 32 | case PLUGIN_GET_DEVICENAME: 33 | { 34 | string = F(PLUGIN_NAME_180); 35 | break; 36 | } 37 | 38 | case PLUGIN_GET_DEVICEVALUENAMES: 39 | { 40 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_180)); 41 | break; 42 | } 43 | case PLUGIN_INIT: 44 | { 45 | int availablePorts=1; 46 | if (Settings.TaskDevicePin1[event->TaskIndex] != -1) 47 | { 48 | pinMode(Settings.TaskDevicePin1[event->TaskIndex], OUTPUT); 49 | availablePorts*=2; 50 | } 51 | if (Settings.TaskDevicePin2[event->TaskIndex] != -1) 52 | { 53 | pinMode(Settings.TaskDevicePin2[event->TaskIndex], OUTPUT); 54 | availablePorts*=2; 55 | } 56 | if (Settings.TaskDevicePin3[event->TaskIndex] != -1) 57 | { 58 | pinMode(Settings.TaskDevicePin3[event->TaskIndex], OUTPUT); 59 | availablePorts*=2; 60 | } 61 | String log = F("Mux available ports : "); 62 | log += availablePorts; 63 | addLog(LOG_LEVEL_INFO,log); 64 | success = true; 65 | break; 66 | } 67 | case PLUGIN_READ: 68 | { 69 | String log = F("ADC : Analog port "); 70 | log += Settings.TaskDevicePort[event->TaskIndex]; 71 | log += F(" mux address: "); 72 | switch( Settings.TaskDevicePort[event->TaskIndex] ) 73 | { 74 | case 1: 75 | digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], LOW); 76 | digitalWrite(Settings.TaskDevicePin2[event->TaskIndex], LOW); 77 | digitalWrite(Settings.TaskDevicePin3[event->TaskIndex], HIGH); 78 | log += " 001 "; 79 | break; 80 | case 2: 81 | digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], LOW); 82 | digitalWrite(Settings.TaskDevicePin2[event->TaskIndex], HIGH); 83 | digitalWrite(Settings.TaskDevicePin3[event->TaskIndex], LOW); 84 | log += " 010 "; 85 | break; 86 | case 3: 87 | digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], LOW); 88 | digitalWrite(Settings.TaskDevicePin2[event->TaskIndex], HIGH); 89 | digitalWrite(Settings.TaskDevicePin3[event->TaskIndex], HIGH); 90 | log += " 011 "; 91 | break; 92 | case 4: 93 | digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], HIGH); 94 | digitalWrite(Settings.TaskDevicePin2[event->TaskIndex], LOW); 95 | digitalWrite(Settings.TaskDevicePin3[event->TaskIndex], LOW); 96 | log += " 100 "; 97 | break; 98 | case 5: 99 | digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], HIGH); 100 | digitalWrite(Settings.TaskDevicePin2[event->TaskIndex], LOW); 101 | digitalWrite(Settings.TaskDevicePin3[event->TaskIndex], HIGH); 102 | log += " 101 "; 103 | break; 104 | case 6: 105 | digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], HIGH); 106 | digitalWrite(Settings.TaskDevicePin2[event->TaskIndex], HIGH); 107 | digitalWrite(Settings.TaskDevicePin3[event->TaskIndex], LOW); 108 | log += " 110 "; 109 | break; 110 | case 7: 111 | digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], HIGH); 112 | digitalWrite(Settings.TaskDevicePin2[event->TaskIndex], HIGH); 113 | digitalWrite(Settings.TaskDevicePin3[event->TaskIndex], HIGH); 114 | log += " 111 "; 115 | break; 116 | default : 117 | digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], LOW); 118 | digitalWrite(Settings.TaskDevicePin2[event->TaskIndex], LOW); 119 | digitalWrite(Settings.TaskDevicePin3[event->TaskIndex], LOW); 120 | log += " 000 "; 121 | } 122 | 123 | UserVar[event->BaseVarIndex] = analogRead(A0); 124 | 125 | log += F("value: "); 126 | log += UserVar[event->BaseVarIndex]; 127 | addLog(LOG_LEVEL_INFO,log); 128 | success = true; 129 | break; 130 | } 131 | } 132 | return success; 133 | } -------------------------------------------------------------------------------- /_P184_GY_US42V2.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //######################### Plugin 184: GY_US42V2 Ultrasonic range finder sensor ############################ 3 | //####################################################################################################### 4 | 5 | #define PLUGIN_184 6 | #define PLUGIN_ID_184 184 7 | #define PLUGIN_NAME_184 "Ultrasonic range finder - GY-US42V2" 8 | #define PLUGIN_VALUENAME1_184 "DISTANCE" 9 | 10 | #define GY_US42V2_ADDRESS (0x70) // default address (0x70 = datasheet address 0xE0) 11 | #define GY_US42V2_CMD_RANGE_COMMAND (0x51) 12 | 13 | boolean Plugin_184(byte function, struct EventStruct *event, String& string) 14 | { 15 | boolean success = false; 16 | 17 | switch (function) 18 | { 19 | 20 | case PLUGIN_DEVICE_ADD: 21 | { 22 | Device[++deviceCount].Number = PLUGIN_ID_184; 23 | Device[deviceCount].Type = DEVICE_TYPE_I2C; 24 | Device[deviceCount].VType = SENSOR_TYPE_SINGLE; 25 | Device[deviceCount].Ports = 0; 26 | Device[deviceCount].PullUpOption = false; 27 | Device[deviceCount].InverseLogicOption = false; 28 | Device[deviceCount].FormulaOption = true; 29 | Device[deviceCount].ValueCount = 1; 30 | Device[deviceCount].SendDataOption = true; 31 | Device[deviceCount].TimerOption = true; 32 | Device[deviceCount].GlobalSyncOption = true; 33 | break; 34 | } 35 | 36 | case PLUGIN_GET_DEVICENAME: 37 | { 38 | string = F(PLUGIN_NAME_184); 39 | break; 40 | } 41 | 42 | case PLUGIN_GET_DEVICEVALUENAMES: 43 | { 44 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_184)); 45 | break; 46 | } 47 | 48 | case PLUGIN_WEBFORM_LOAD: 49 | { 50 | success = true; 51 | break; 52 | } 53 | 54 | case PLUGIN_WEBFORM_SAVE: 55 | { 56 | success = true; 57 | break; 58 | } 59 | 60 | case PLUGIN_INIT: 61 | { 62 | Plugin_184_begin(); 63 | success = true; 64 | break; 65 | } 66 | 67 | case PLUGIN_READ: 68 | { 69 | uint16_t value; 70 | value = Plugin_184_getDistance(); 71 | UserVar[event->BaseVarIndex] = value; 72 | String log = F("P184 : distance = "); 73 | log += value; 74 | log += F(" cm"); 75 | addLog(LOG_LEVEL_INFO,log); 76 | success = true; 77 | break; 78 | } 79 | } 80 | return success; 81 | } 82 | 83 | //**************************************************************************/ 84 | // Sensor setup 85 | //**************************************************************************/ 86 | void Plugin_184_begin(void) 87 | { 88 | } 89 | 90 | //**************************************************************************/ 91 | // Report distance 92 | //**************************************************************************/ 93 | uint16_t Plugin_184_getDistance() 94 | { 95 | uint16_t value = 0; 96 | 97 | Wire.beginTransmission(GY_US42V2_ADDRESS); 98 | Wire.write(GY_US42V2_CMD_RANGE_COMMAND); 99 | Wire.endTransmission(); 100 | 101 | delay(70); // transmit -> receive turnaround time (up to 65ms) 102 | 103 | Wire.requestFrom(GY_US42V2_ADDRESS, 2); 104 | if (Wire.available() > 1) { 105 | value = ((Wire.read() << 8) | Wire.read()); 106 | } 107 | 108 | return value; 109 | } 110 | -------------------------------------------------------------------------------- /_P203_Feeder.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //################################ Plugin 203: Pet feeder ########################################### 3 | //####################################################################################################### 4 | //############################### Plugin for ESP Easy by DatuX ############################### 5 | //################################### http://www.datux.nl ############################################## 6 | //####################################################################################################### 7 | 8 | 9 | 10 | #ifdef PLUGIN_BUILD_TESTING 11 | 12 | #define PLUGIN_203 13 | #define PLUGIN_ID_203 203 14 | #define PLUGIN_NAME_203 "Automated pet feeder [TESTING]" 15 | 16 | #ifndef CONFIG 17 | #define CONFIG(n) (Settings.TaskDevicePluginConfig[event->TaskIndex][n]) 18 | #endif 19 | 20 | 21 | 22 | //to not scare the cat of sudden motor movements 23 | //attack and sustain in mS 24 | void ramped_pulse(byte pin, unsigned long attack, unsigned long sustain) 25 | { 26 | 27 | //attack 28 | unsigned long start_time=millis(); 29 | while (millis()-start_time < attack) 30 | { 31 | analogWrite(pin, ((millis()-start_time) * PWMRANGE /attack ) ); 32 | yield(); 33 | } 34 | 35 | //sustain 36 | analogWrite(pin, PWMRANGE); 37 | // digitalWrite(pin, HIGH); 38 | delay(sustain); 39 | 40 | //releaase 41 | start_time=millis(); 42 | while (millis()-start_time < attack) 43 | { 44 | analogWrite(pin, PWMRANGE-(((millis()-start_time) * PWMRANGE / attack) )); 45 | yield(); 46 | } 47 | 48 | //make sure its really off 49 | analogWrite(pin, 0); 50 | // digitalWrite(pin, LOW); 51 | } 52 | 53 | boolean Plugin_203(byte function, struct EventStruct *event, String& string) 54 | { 55 | boolean success = false; 56 | 57 | switch (function) 58 | { 59 | case PLUGIN_DEVICE_ADD: 60 | { 61 | Device[++deviceCount].Number = PLUGIN_ID_203; 62 | Device[deviceCount].Type = DEVICE_TYPE_TRIPLE; 63 | Device[deviceCount].VType = SENSOR_TYPE_SINGLE; 64 | Device[deviceCount].Ports = 0; 65 | Device[deviceCount].PullUpOption = false; 66 | Device[deviceCount].InverseLogicOption = false; 67 | Device[deviceCount].FormulaOption = false; 68 | Device[deviceCount].ValueCount = 1; 69 | Device[deviceCount].SendDataOption = true; 70 | Device[deviceCount].TimerOption = false; 71 | Device[deviceCount].GlobalSyncOption = false; 72 | break; 73 | } 74 | 75 | case PLUGIN_GET_DEVICENAME: 76 | { 77 | string = F(PLUGIN_NAME_203); 78 | break; 79 | } 80 | 81 | case PLUGIN_GET_DEVICEVALUENAMES: 82 | { 83 | break; 84 | } 85 | 86 | case PLUGIN_WEBFORM_LOAD: 87 | { 88 | 89 | addFormNumericBox(F("Rotate forward time"), F("forward"), CONFIG(0), 0, 60000); 90 | addUnit(F("ms")); 91 | addFormNumericBox(F("Rotate reverse time"), F("reverse"), CONFIG(1), 0, 60000); 92 | addUnit(F("ms")); 93 | 94 | 95 | 96 | success = true; 97 | break; 98 | } 99 | 100 | case PLUGIN_WEBFORM_SAVE: 101 | { 102 | CONFIG(0) = getFormItemInt(F("forward")); 103 | CONFIG(1) = getFormItemInt(F("reverse")); 104 | 105 | success = true; 106 | break; 107 | } 108 | 109 | case PLUGIN_INIT: 110 | { 111 | pinMode(Settings.TaskDevicePin1[event->TaskIndex],OUTPUT); 112 | pinMode(Settings.TaskDevicePin2[event->TaskIndex],OUTPUT); 113 | pinMode(Settings.TaskDevicePin3[event->TaskIndex],OUTPUT); 114 | // analogWriteFreq(30000); 115 | digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], LOW); //disable 116 | success = true; 117 | break; 118 | } 119 | 120 | 121 | 122 | case PLUGIN_WRITE: 123 | { 124 | 125 | String command = parseString(string, 1); 126 | 127 | if (command == F("feed")) 128 | { 129 | // for(int i=0; iPar1; i++) 130 | { 131 | //forward 132 | // digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], HIGH); //enable 133 | digitalWrite(Settings.TaskDevicePin2[event->TaskIndex], HIGH); 134 | digitalWrite(Settings.TaskDevicePin3[event->TaskIndex], LOW); 135 | 136 | ramped_pulse(Settings.TaskDevicePin1[event->TaskIndex], event->Par1, CONFIG(0)); 137 | // delay(CONFIG(0)); 138 | 139 | //pause 140 | // digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], LOW); //disable 141 | // delay(100); 142 | 143 | //reverse 144 | // digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], HIGH); //enable 145 | digitalWrite(Settings.TaskDevicePin2[event->TaskIndex], LOW); 146 | digitalWrite(Settings.TaskDevicePin3[event->TaskIndex], HIGH); 147 | ramped_pulse(Settings.TaskDevicePin1[event->TaskIndex], 1000, CONFIG(1)); 148 | // delay(CONFIG(1)); 149 | 150 | //pause 151 | // digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], LOW); //disable 152 | // delay(100); 153 | 154 | } 155 | 156 | 157 | } 158 | 159 | 160 | success=true; 161 | break; 162 | } 163 | 164 | } 165 | return success; 166 | } 167 | 168 | #endif 169 | -------------------------------------------------------------------------------- /_P211_MPU6050.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //################################ Plugin 211: MPU6050: motion detection ################################ 3 | //####################################################################################################### 4 | // 5 | // This plugin uses the MPU6050 accelerometer & gyro to detect motion. The motion threshold can be 6 | // configured directly on the UI. 7 | // 8 | // NOTE: requires the following libraries: 9 | // 10 | // * MPU6050: https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/MPU6050 11 | // * I2CDEV: https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/I2Cdev 12 | //####################################################################################################### 13 | 14 | #include "Wire.h" 15 | #include "MPU6050.h" 16 | 17 | MPU6050 *mpu; 18 | 19 | int16_t motion = 0; 20 | 21 | #define PLUGIN_211 22 | #define PLUGIN_ID_211 211 23 | #define PLUGIN_NAME_211 "Motion Detection - MPU6050" 24 | #define PLUGIN_VALUENAME1_211 "Motion" 25 | 26 | #define MPU6050_ADDRESS 0x68 // I2C address 27 | 28 | boolean Plugin_211(byte function, struct EventStruct *event, String& string) 29 | { 30 | boolean success = false; 31 | 32 | switch (function) 33 | { 34 | case PLUGIN_DEVICE_ADD: 35 | { 36 | Device[++deviceCount].Number = PLUGIN_ID_211; 37 | Device[deviceCount].Type = DEVICE_TYPE_I2C; 38 | Device[deviceCount].VType = SENSOR_TYPE_SINGLE; 39 | Device[deviceCount].Ports = 0; 40 | Device[deviceCount].PullUpOption = false; 41 | Device[deviceCount].InverseLogicOption = false; 42 | Device[deviceCount].FormulaOption = false; 43 | Device[deviceCount].ValueCount = 1; 44 | Device[deviceCount].SendDataOption = true; 45 | Device[deviceCount].TimerOption = true; 46 | Device[deviceCount].GlobalSyncOption = true; 47 | break; 48 | } 49 | 50 | case PLUGIN_GET_DEVICENAME: 51 | { 52 | string = F(PLUGIN_NAME_211); 53 | break; 54 | } 55 | 56 | case PLUGIN_GET_DEVICEVALUENAMES: 57 | { 58 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_211)); 59 | break; 60 | } 61 | 62 | case PLUGIN_WEBFORM_LOAD: 63 | { 64 | char tmpString[128]; 65 | sprintf_P(tmpString, PSTR("Motion threshold:"), ExtraTaskSettings.TaskDevicePluginConfigLong[0]); 66 | string += tmpString; 67 | 68 | sprintf_P(tmpString, PSTR("Stop threshold:"), ExtraTaskSettings.TaskDevicePluginConfigLong[1]); 69 | string += tmpString; 70 | 71 | success = true; 72 | break; 73 | } 74 | 75 | case PLUGIN_WEBFORM_SAVE: 76 | { 77 | int16_t motion_threshold = WebServer.arg("plugin_211_motion_threshold").toInt(); 78 | ExtraTaskSettings.TaskDevicePluginConfigLong[0] = motion_threshold; 79 | 80 | int16_t stop_threshold = WebServer.arg("plugin_211_stop_threshold").toInt(); 81 | ExtraTaskSettings.TaskDevicePluginConfigLong[1] = stop_threshold; 82 | 83 | mpu->setMotionDetectionThreshold(motion_threshold); 84 | mpu->setZeroMotionDetectionThreshold(stop_threshold); 85 | 86 | success = true; 87 | break; 88 | } 89 | 90 | case PLUGIN_INIT: 91 | { 92 | addLog(LOG_LEVEL_INFO, "MPU-6050 : Init"); 93 | 94 | // Load saved threshold settings. 95 | LoadTaskSettings(event->TaskIndex); 96 | 97 | // Initialize MPU sensor. 98 | mpu = new MPU6050(MPU6050_ADDRESS); 99 | mpu->initialize(); 100 | 101 | // Enable Zero Motion Detection interrupt. 102 | mpu->setIntFreefallEnabled(false); 103 | mpu->setIntZeroMotionEnabled(false); 104 | mpu->setIntMotionEnabled(false); 105 | 106 | // Set accelerometer power-on delay (3 ms). 107 | mpu->setAccelerometerPowerOnDelay(3); 108 | 109 | // Set high-pass filter configuration in mode 1 (5Hz) for improved motion detection. 110 | mpu->setDHPFMode(1); 111 | 112 | // Set motion detection event acceleration threshold. Motion is detected when the 113 | // absolute value of any of the accelerometer measurements exceeds this Motion 114 | // detection threshold. 115 | mpu->setMotionDetectionThreshold(ExtraTaskSettings.TaskDevicePluginConfigLong[0] ?: 4); 116 | 117 | // Set zero motion detection event acceleration threshold. 118 | mpu->setZeroMotionDetectionThreshold(ExtraTaskSettings.TaskDevicePluginConfigLong[1] ?: 4); 119 | 120 | // Set motion detection event duration threshold. The Motion detection duration 121 | // counter increments when the absolute value of any of the accelerometer 122 | // measurements exceeds the Motion detection threshold (Register 31). The Motion 123 | // detection interrupt is triggered when the Motion detection counter reaches 124 | // the time count specified in this register. 125 | mpu->setMotionDetectionDuration(4); 126 | 127 | // Set zero motion detection event duration threshold. The Zero Motion duration counter 128 | // increments while the absolute value of the accelerometer measurements are each 129 | // less than the detection threshold (Register 33). The Zero Motion interrupt is 130 | // triggered when the Zero Motion duration counter reaches the time count specified 131 | // in this register. 132 | mpu->setZeroMotionDetectionDuration(4); 133 | 134 | success = true; 135 | break; 136 | } 137 | 138 | case PLUGIN_ONCE_A_SECOND: 139 | { 140 | // If any axis has motion, then increment motion. 141 | if (mpu->getMotionStatus() > 1) { 142 | motion++; 143 | } 144 | 145 | success = true; 146 | break; 147 | } 148 | 149 | case PLUGIN_READ: 150 | { 151 | // Convert motion to a percentage that represents how much motion there was 152 | // in the last X seconds, where X is the configured polling delay. 153 | UserVar[event->BaseVarIndex] = ((float) motion / Settings.TaskDeviceTimer[event->TaskIndex]) * 100; 154 | 155 | // Reset motion. 156 | motion = 0; 157 | 158 | String log = F("MPU6050 : Reporting motion: "); 159 | log += UserVar[event->BaseVarIndex]; 160 | addLog(LOG_LEVEL_DEBUG, log); 161 | 162 | sendData(event); 163 | 164 | success = true; 165 | break; 166 | } 167 | } 168 | 169 | return success; 170 | } 171 | -------------------------------------------------------------------------------- /_P212_MY9291.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //#################################### Plugin 212: MY9291 Basic ######################################### 3 | //####################################################################################################### 4 | 5 | // The MY9291 led driver is a device found in smart lightbulbs such as the AI Thinker AILight and many 6 | // other bulbs that are either clones or licenced designs. This plugin offers RGBW control of these 7 | // bulbs. The plugin is designed to control 4 channels, the MY9291 can be used in 3 channel mode as well 8 | // 3 Channel should work I think, but is untested 9 | 10 | // This plugin requires modification of the EventStruct in order to function. A Par4 integer needs to be 11 | // added currently. The parseCommandString function will also need to be updated as well. 12 | 13 | // The MY9291 library (from tinkerman) is required for this plugin to function correctly. 14 | // https://github.com/xoseperez/my9291 15 | 16 | // Although the MY9291 led driver can operate in 8bit, 16bit and 32bit modes, the default for the MY9291 17 | // is used. This offers 8bit control of each channel i.e. a 32bit command is sent on each change 18 | 19 | // List of commands: 20 | // (1) MY9291,,,, 21 | 22 | // Usage: 23 | // (1): Set RGBW Color to specified LED number (eg. MY9291,255,255,255,20) 24 | 25 | #include 26 | my9291 *Plugin_212_rgbw; 27 | 28 | #define PLUGIN_212 29 | #define PLUGIN_ID_212 212 30 | #define PLUGIN_NAME_212 "MY9291" 31 | #define PLUGIN_VALUENAME1_212 "RGB" 32 | 33 | boolean Plugin_212(byte function, struct EventStruct *event, String& string) 34 | { 35 | boolean success = false; 36 | 37 | switch (function) 38 | { 39 | 40 | case PLUGIN_DEVICE_ADD: 41 | { 42 | Device[++deviceCount].Number = PLUGIN_ID_212; 43 | Device[deviceCount].Type = DEVICE_TYPE_DUAL; 44 | Device[deviceCount].Custom = true; 45 | Device[deviceCount].TimerOption = false; 46 | break; 47 | } 48 | 49 | case PLUGIN_GET_DEVICENAME: 50 | { 51 | string = F(PLUGIN_NAME_212); 52 | break; 53 | } 54 | 55 | case PLUGIN_GET_DEVICEVALUENAMES: 56 | { 57 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_212)); 58 | break; 59 | } 60 | 61 | case PLUGIN_WEBFORM_LOAD: 62 | { 63 | char tmpString[128]; 64 | 65 | string += F("DI Pin:"); 66 | addPinSelect(false, string, "taskdevicepin1", Settings.TaskDevicePin1[event->TaskIndex]); 67 | string += F("DCKI Pin:"); 68 | addPinSelect(false, string, "taskdevicepin2", Settings.TaskDevicePin2[event->TaskIndex]); 69 | 70 | success = true; 71 | break; 72 | } 73 | 74 | case PLUGIN_WEBFORM_SAVE: 75 | { 76 | success = true; 77 | break; 78 | } 79 | 80 | case PLUGIN_INIT: 81 | { 82 | if (!Plugin_212_rgbw) 83 | { 84 | Plugin_212_rgbw = new my9291(Settings.TaskDevicePin1[event->TaskIndex], Settings.TaskDevicePin2[event->TaskIndex], MY9291_COMMAND_DEFAULT); 85 | Plugin_212_rgbw->setState(true); 86 | } 87 | success = true; 88 | break; 89 | } 90 | 91 | case PLUGIN_WRITE: 92 | { 93 | if (Plugin_212_rgbw) 94 | { 95 | String tmpString = string; 96 | int argIndex = tmpString.indexOf(','); 97 | if (argIndex) 98 | tmpString = tmpString.substring(0, argIndex); 99 | 100 | if (tmpString.equalsIgnoreCase(F("MY9291"))) 101 | { 102 | Plugin_212_rgbw->setColor((my9291_color_t) { event->Par1, event->Par2, event->Par3, event->Par4 }); 103 | Plugin_212_rgbw->setState(true); 104 | success = true; 105 | } 106 | } 107 | 108 | break; 109 | } 110 | return success; 111 | } 112 | } 113 | 114 | -------------------------------------------------------------------------------- /_P213_VEML6070: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //#################### Plugin 213 VEML6070 Ultraviolet Sensor ####################### 3 | //#################### based on Adafruit Library ####################### 4 | //####################################################################################################### 5 | 6 | #define PLUGIN_213 7 | #define PLUGIN_ID_213 213 8 | #define PLUGIN_NAME_213 "UV - VEML6070" 9 | #define PLUGIN_VALUENAME1_213 "UV" 10 | 11 | // See also VEML6070 data sheet:http://www.vishay.com/docs/84277/veml6070.pdf 12 | // 13 | //////////////////////////// 14 | // VEML6070 Registers/addresses // 15 | //////////////////////////// 16 | 17 | #define VEML6070_ADDR_ARA 0x19 >> 1 // shift by one to get correct register addresses 18 | #define VEML6070_ADDR_CMD 0x70 >> 1 19 | #define VEML6070_ADDR_DATA_LSB 0x71 >> 1 20 | #define VEML6070_ADDR_DATA_MSB 0x73 >> 1 21 | 22 | // really unusual way of getting data, your read from two different addrs! 23 | #define VEML6070_ADDR_H 0x39 24 | #define VEML6070_ADDR_L 0x38 25 | 26 | // VEML6070 command register bits 27 | #define VEML6070_CMD_DISABLE 0x01 28 | #define VEML6070_CMD_WDM 0x02 29 | 30 | // three different integration times 31 | typedef enum veml6070_integrationtime { 32 | VEML6070_HALF_T, 33 | VEML6070_1_T, 34 | VEML6070_2_T, 35 | VEML6070_4_T, 36 | } veml6070_integrationtime_t; 37 | 38 | bool Plugin_213_begin(veml6070_integrationtime_t itime); 39 | uint16_t Plugin_213_readUV(); 40 | uint16_t Plugin_213_convert_to_risk_level(uint16_t uvs_step); 41 | 42 | boolean Plugin_213(byte function, struct EventStruct *event, String& string) 43 | { 44 | boolean success = false; 45 | 46 | switch (function) 47 | { 48 | case PLUGIN_DEVICE_ADD: 49 | { 50 | Device[++deviceCount].Number = PLUGIN_ID_213; 51 | Device[deviceCount].Type = DEVICE_TYPE_I2C; 52 | Device[deviceCount].VType = SENSOR_TYPE_SINGLE; 53 | Device[deviceCount].Ports = 0; 54 | Device[deviceCount].PullUpOption = false; 55 | Device[deviceCount].InverseLogicOption = false; 56 | Device[deviceCount].FormulaOption = true; 57 | Device[deviceCount].ValueCount = 1; 58 | Device[deviceCount].SendDataOption = true; 59 | Device[deviceCount].TimerOption = true; 60 | Device[deviceCount].GlobalSyncOption = true; 61 | break; 62 | } 63 | 64 | case PLUGIN_GET_DEVICENAME: 65 | { 66 | string = F(PLUGIN_NAME_213); 67 | break; 68 | } 69 | 70 | case PLUGIN_GET_DEVICEVALUENAMES: 71 | { 72 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_213)); 73 | break; 74 | } 75 | 76 | case PLUGIN_READ: 77 | { 78 | if (!Plugin_213_begin(VEML6070_1_T)) { 79 | String log = F("VEML6070: No available!"); 80 | addLog(LOG_LEVEL_INFO, log); 81 | UserVar[event->BaseVarIndex] = NAN; 82 | success = false; 83 | } else { 84 | String s = ""; 85 | uint16_t uv; 86 | 87 | uv = Plugin_213_readUV(); 88 | if (isnan(uv) || uv == (-1) || uv == 65535) { 89 | String log = F("VEML6070: no data read!"); 90 | addLog(LOG_LEVEL_INFO, log); 91 | UserVar[event->BaseVarIndex] = NAN; 92 | success = false; 93 | } else { 94 | UserVar[event->BaseVarIndex] = uv; 95 | String log = F("VEML6070: UV: "); 96 | log += UserVar[event->BaseVarIndex]; 97 | addLog(LOG_LEVEL_INFO, log); 98 | success = true; 99 | } 100 | } 101 | 102 | break; 103 | } 104 | 105 | } 106 | return success; 107 | } 108 | 109 | //**************************************************************************/ 110 | // Check VEML6070 presence 111 | //**************************************************************************/ 112 | bool Plugin_213_begin(veml6070_integrationtime_t itime) { 113 | byte error; 114 | 115 | Wire.begin(); 116 | Wire.beginTransmission(VEML6070_ADDR_L); 117 | Wire.write((itime << 2) | VEML6070_CMD_WDM); 118 | error = Wire.endTransmission(); 119 | delay(500); 120 | 121 | return (error == 0); 122 | } 123 | 124 | /* 125 | Die Sonnenleistung berechne ich mit einer Simplen Formel 126 | WATT = ((uv.readUV() * 2.5) / 187) * 10; 127 | Allerdings benutze ich einen VEML6070 UV Sensor via I2C. 128 | Kann aber nicht bestätigen ob dieser Wert auch stimmt aber laut meinem Gefühl könnte es schon hinkommen. 129 | */ 130 | // UV = uv.readUV()/684.75; 131 | //  WATT = ((uv.readUV() * 2.5) / 187) * 10; 132 | 133 | uint16_t Plugin_213_readUV() { 134 | if (Wire.requestFrom(VEML6070_ADDR_H, 1) != 1) return -1; 135 | uint16_t uvi = Wire.read(); 136 | uvi <<= 8; 137 | if (Wire.requestFrom(VEML6070_ADDR_L, 1) != 1) return -1; 138 | uvi |= Wire.read(); 139 | 140 | return uvi; 141 | } 142 | 143 | uint16_t Plugin_213_convert_to_risk_level(uint16_t uvs_step) { 144 | uint16_t risk_level_mapping_table[4] = {2241, 4482, 5976, 8217}; 145 | uint16_t i; 146 | for (i = 0; i < 4; i++) { 147 | if (uvs_step <= risk_level_mapping_table[i]) { 148 | break; 149 | } 150 | } 151 | return i; 152 | } 153 | -------------------------------------------------------------------------------- /_P217_ACS712AC.ino: -------------------------------------------------------------------------------- 1 | 2 | #ifdef PLUGIN_BUILD_DEV 3 | 4 | //Plugin to measure AC current using a ACS712 module. Very much a work in progress. 5 | //For my lab, i have used R1=1200 and R2=2000 6 | #define PLUGIN_217 7 | #define PLUGIN_ID_217 217 //plugin id 8 | #define PLUGIN_NAME_217 "Energy (AC) - ACS712 [DEVELOPMENT]" //"Plugin Name" is what will be dislpayed in the selection list 9 | #define PLUGIN_VALUENAME1_217 "A" //variable output of the plugin. The label is in quotation marks 10 | #define PLUGIN_217_DEBUG true //set to true for extra log info in the debug 11 | #define PLUGIN_217_MODEL_5A 185 12 | #define PLUGIN_217_MODEL_20A 100 13 | #define PLUGIN_217_MODEL_30A 66 14 | 15 | boolean Plugin_217(byte function, struct EventStruct *event, String& string) 16 | { 17 | boolean success = false; 18 | switch (function) 19 | { 20 | case PLUGIN_DEVICE_ADD: 21 | { 22 | 23 | //This case defines the device characteristics, edit appropriately 24 | 25 | Device[++deviceCount].Number = PLUGIN_ID_217; 26 | Device[deviceCount].Type = DEVICE_TYPE_ANALOG; //how the device is connected 27 | Device[deviceCount].VType = SENSOR_TYPE_SINGLE; //type of value the plugin will return, used only for Domoticz 28 | Device[deviceCount].Ports = 0; 29 | Device[deviceCount].PullUpOption = false; 30 | Device[deviceCount].InverseLogicOption = false; 31 | Device[deviceCount].FormulaOption = false; 32 | Device[deviceCount].ValueCount = 1; //number of output variables. The value should match the number of keys PLUGIN_VALUENAME1_xxx 33 | Device[deviceCount].SendDataOption = true; 34 | Device[deviceCount].TimerOption = true; 35 | Device[deviceCount].TimerOptional = false; 36 | Device[deviceCount].GlobalSyncOption = true; 37 | Device[deviceCount].DecimalsOnly = true; 38 | break; 39 | } 40 | case PLUGIN_GET_DEVICENAME: 41 | { 42 | //return the device name 43 | string = F(PLUGIN_NAME_217); 44 | break; 45 | } 46 | case PLUGIN_GET_DEVICEVALUENAMES: 47 | { 48 | //called when the user opens the module configuration page 49 | //it allows to add a new row for each output variable of the plugin 50 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_217)); 51 | break; 52 | } 53 | case PLUGIN_WEBFORM_LOAD: 54 | { 55 | String model[3]; 56 | model[0] = F("ACS712 5A"); 57 | model[1] = F("ACS712 20A"); 58 | model[2] = F("ACS712 30A"); 59 | byte modelchoice = Settings.TaskDevicePluginConfig[event->TaskIndex][0]; 60 | int modelValues[3] = { PLUGIN_217_MODEL_5A, PLUGIN_217_MODEL_20A, PLUGIN_217_MODEL_30A }; 61 | addFormSubHeader(F("Model selection")); 62 | addFormSelector(F("ACS712 model"), F("plugin_217_model"), 3, model, modelValues, modelchoice); 63 | addFormSubHeader(F("Sampling period")); 64 | addFormNumericBox(F("Sampling period in ms"), F("plugin_217_sampling_period"), ExtraTaskSettings.TaskDevicePluginConfigLong[2]); 65 | addFormSubHeader(F("Voltage divider values")); 66 | addFormNumericBox(F("R1 Value, Ohms"), F("plugin_217_r1"), ExtraTaskSettings.TaskDevicePluginConfigLong[0]); 67 | addFormNumericBox(F("R2 Value, Ohms"), F("plugin_217_r2"), ExtraTaskSettings.TaskDevicePluginConfigLong[1]); 68 | addFormSubHeader(F("Result offset")); 69 | 70 | success = true; 71 | break; 72 | } 73 | case PLUGIN_WEBFORM_SAVE: 74 | { 75 | //this case defines the code to be executed when the form is submitted 76 | //the plugin settings should be saved to Settings.TaskDevicePluginConfig[event->TaskIndex][x] 77 | //ping configuration should be read from Settings.TaskDevicePin1[event->TaskIndex] and stored 78 | Settings.TaskDevicePluginConfig[event->TaskIndex][0] = getFormItemInt(F("plugin_217_model")); 79 | ExtraTaskSettings.TaskDevicePluginConfigLong[0] = getFormItemInt(F("plugin_217_r1")); 80 | ExtraTaskSettings.TaskDevicePluginConfigLong[1] = getFormItemInt(F("plugin_217_r2")); 81 | ExtraTaskSettings.TaskDevicePluginConfigLong[2] = getFormItemInt(F("plugin_217_sampling_period")); 82 | //after the form has been saved successfuly, set success and break 83 | success = true; 84 | break; 85 | 86 | } 87 | case PLUGIN_INIT: 88 | { 89 | //this case defines code to be executed when the plugin is initialised 90 | 91 | //after the plugin has been initialised successfuly, set success and break 92 | success = true; 93 | break; 94 | 95 | } 96 | 97 | case PLUGIN_READ: 98 | { 99 | 100 | int mVperAmp = Settings.TaskDevicePluginConfig[event->TaskIndex][0]; 101 | float R1 = ExtraTaskSettings.TaskDevicePluginConfigLong[0]; 102 | float R2 = ExtraTaskSettings.TaskDevicePluginConfigLong[1]; 103 | double Voltage = 0; 104 | double VRMS = 0; 105 | double AmpsRMS = 0; 106 | double VoltageSensor = 0; 107 | Voltage = p217_get_VPP(); 108 | 109 | 110 | VoltageSensor = (Voltage*(R2/R1)); 111 | VRMS = (VoltageSensor/2.0) *0.707; 112 | AmpsRMS = (VRMS * 1000)/mVperAmp; 113 | UserVar[event->BaseVarIndex] = (float)AmpsRMS; 114 | #ifdef PLUGIN_217_DEBUG 115 | String log = ""; 116 | log = "Voltage:"; 117 | log += String(Voltage); 118 | log += ", R1:"; 119 | log += String(R1); 120 | log += ", R2:"; 121 | log += String(R2); 122 | log += " VoltageSensor:"; 123 | log += String(VoltageSensor); 124 | log += ", AmpsRMS:"; 125 | log += String(AmpsRMS); 126 | addLog(LOG_LEVEL_INFO,String(log)); 127 | #endif 128 | success = true; 129 | break; 130 | 131 | } 132 | case PLUGIN_EXIT: 133 | { 134 | //perform cleanup tasks here. For example, free memory 135 | 136 | break; 137 | 138 | } 139 | 140 | case PLUGIN_ONCE_A_SECOND: 141 | { 142 | //code to be executed once a second. Tasks which do not require fast response can be added here 143 | success = true; 144 | } 145 | } 146 | return success; 147 | } 148 | 149 | float p217_get_VPP() 150 | { 151 | long timePassedSince(unsigned long timestamp); 152 | boolean timeOutReached(unsigned long timer); 153 | float sampling_period = ExtraTaskSettings.TaskDevicePluginConfigLong[2]; 154 | const int sensorIn = A0; 155 | float result; 156 | int readValue; //value read from the sensor 157 | int maxValue = 0; // store max value here 158 | int minValue = 1024; // store min value here 159 | uint32_t start_time = millis(); 160 | uint32_t nice_time = millis(); 161 | 162 | while(timePassedSince(start_time) < sampling_period) //sample the period specified 163 | { 164 | readValue = analogRead(sensorIn); 165 | //After a friendly tip in my pull request it was suggested that one should 166 | //call delay(0) periodically to increase stability. So, lets do that every 20ms 167 | if (timePassedSince(nice_time) > 20 ){ 168 | delay(0); 169 | nice_time = millis(); 170 | } 171 | 172 | // see if you have a new maxValue 173 | if (readValue > maxValue) 174 | { 175 | /*record the maximum sensor value*/ 176 | maxValue = readValue; 177 | } 178 | if (readValue < minValue) 179 | { 180 | /*record the maximum sensor value*/ 181 | minValue = readValue; 182 | } 183 | } 184 | 185 | // Subtract min from max 186 | result = ((maxValue - minValue) * 5.0)/1024.0; 187 | 188 | return result; 189 | 190 | } 191 | #endif 192 | -------------------------------------------------------------------------------- /_P246_DAC.ino: -------------------------------------------------------------------------------- 1 | #ifdef USES_P246 2 | //####################################################################################################### 3 | //#################################### Plugin 246: ESP32 DAC ############################################ 4 | // 5 | // DAC for ESP32 6 | // 7 | //####################################################################################################### 8 | 9 | #include "_Plugin_Helper.h" 10 | 11 | #define PLUGIN_246 12 | #define PLUGIN_ID_246 246 13 | #define PLUGIN_NAME_246 "ESP32 DAC" 14 | #define PLUGIN_VALUENAME1_246 "Output" 15 | 16 | #define P246_DAC_VALUE UserVar[event->BaseVarIndex] 17 | 18 | // Start: add to Hardware.ino 19 | #ifdef ESP32 20 | 21 | // Get DAC related info for a given GPIO pin 22 | // @param gpio_pin GPIO pin number 23 | // @param dac Number of DAC unit 24 | bool getDAC_gpio_info(int gpio_pin, int& dac) 25 | { 26 | switch (gpio_pin) { 27 | case 25: dac = 1; break; 28 | case 26: dac = 2; break; 29 | default: 30 | return false; 31 | } 32 | return true; 33 | } 34 | 35 | #endif 36 | // End: add to Hardware.ino 37 | 38 | // Start: add to Misc.ino 39 | #ifdef ESP32 40 | 41 | String formatGpioName_DAC(int gpio_pin) { 42 | int dac; 43 | if (getDAC_gpio_info(gpio_pin, dac)) { 44 | return String(F("DAC")) + dac; 45 | } 46 | return ""; 47 | } 48 | 49 | #endif 50 | // End: add to Misc.ino 51 | 52 | // Start: add to WebServer_Markup.ino 53 | #ifdef ESP32 54 | 55 | void addDAC_PinSelect(const String& id, int choice) 56 | { 57 | int NR_ITEMS_PIN_DROPDOWN = 2; 58 | String *gpio_labels = new String[NR_ITEMS_PIN_DROPDOWN]; 59 | int *gpio_numbers = new int[NR_ITEMS_PIN_DROPDOWN]; 60 | 61 | int i = 0; 62 | int gpio = -1; 63 | 64 | while (i < NR_ITEMS_PIN_DROPDOWN && gpio <= MAX_GPIO) { 65 | int pinnr = -1; 66 | bool input, output, warning; 67 | 68 | if (getGpioInfo(gpio, pinnr, input, output, warning)) { 69 | int dac; 70 | if (getDAC_gpio_info(gpio, dac)) { 71 | gpio_labels[i] = formatGpioName_DAC(gpio); 72 | if (dac != 0) { 73 | gpio_labels[i] += F(" / "); 74 | gpio_labels[i] += createGPIO_label(gpio, pinnr, input, output, warning); 75 | } 76 | gpio_numbers[i] = gpio; 77 | ++i; 78 | } 79 | } 80 | ++gpio; 81 | } 82 | bool forI2C = false; 83 | renderHTMLForPinSelect(gpio_labels, gpio_numbers, forI2C, id, choice, i); 84 | delete[] gpio_numbers; 85 | delete[] gpio_labels; 86 | } 87 | 88 | #endif 89 | // End: add to WebServer_Markup.ino 90 | 91 | 92 | boolean Plugin_246(byte function, struct EventStruct * event, String& string) 93 | { 94 | boolean success = false; 95 | 96 | switch (function) 97 | { 98 | case PLUGIN_DEVICE_ADD: 99 | { 100 | Device[++deviceCount].Number = PLUGIN_ID_246; 101 | Device[deviceCount].Type = DEVICE_TYPE_SINGLE; 102 | Device[deviceCount].Custom = true; 103 | Device[deviceCount].ValueCount = 1; 104 | break; 105 | } 106 | 107 | case PLUGIN_GET_DEVICENAME: 108 | { 109 | string = F(PLUGIN_NAME_246); 110 | break; 111 | } 112 | 113 | case PLUGIN_GET_DEVICEVALUENAMES: 114 | { 115 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_246)); 116 | break; 117 | } 118 | 119 | case PLUGIN_WEBFORM_LOAD: 120 | { 121 | addHtml(F("1st GPIO:")); 122 | addDAC_PinSelect(F("taskdevicepin1"), CONFIG_PIN1); 123 | 124 | success = true; 125 | break; 126 | } 127 | 128 | case PLUGIN_WEBFORM_SAVE: 129 | { 130 | success = true; 131 | break; 132 | } 133 | 134 | case PLUGIN_INIT: 135 | { 136 | success = true; 137 | break; 138 | } 139 | 140 | case PLUGIN_READ: 141 | { 142 | success = true; 143 | break; 144 | } 145 | 146 | case PLUGIN_WRITE: 147 | { 148 | String command = parseString(string, 1); 149 | 150 | if (command == F("dac")) { 151 | int dac; 152 | if (getDAC_gpio_info(CONFIG_PIN1, dac) && dac == event->Par1) { 153 | int value = min(255, max(0, event->Par2)); 154 | P246_DAC_VALUE = value; 155 | dacWrite(CONFIG_PIN1, value); 156 | addLog(LOG_LEVEL_DEBUG, formatGpioName_DAC(CONFIG_PIN1) + 157 | String(F(" : Output ")) + String(value)); 158 | 159 | success = true; 160 | } 161 | } 162 | break; 163 | } 164 | } 165 | 166 | return success; 167 | } 168 | 169 | #endif // USES_P246 170 | -------------------------------------------------------------------------------- /_P248_AHT.ino: -------------------------------------------------------------------------------- 1 | #include "_Plugin_Helper.h" 2 | #ifdef USES_P248 3 | 4 | // ####################################################################################################### 5 | // ######################## Plugin 248 AHT I2C Temperature and Humidity Sensor ########################## 6 | // ####################################################################################################### 7 | // data sheet AHT10: https://wiki.liutyi.info/display/ARDUINO/AHT10 8 | // device AHT10: http://www.aosong.com/en/products-40.html 9 | // device and manual AHT20: http://www.aosong.com/en/products-32.html 10 | // device and manual AHT21: http://www.aosong.com/en/products-60.html 11 | 12 | #include "src/PluginStructs/P248_data_struct.h" 13 | 14 | #define PLUGIN_248 15 | #define PLUGIN_ID_248 248 16 | #define PLUGIN_NAME_248 "Environment - AHT10/20/21 [TESTING]" 17 | #define PLUGIN_VALUENAME1_248 "Temperature" 18 | #define PLUGIN_VALUENAME2_248 "Humidity" 19 | 20 | 21 | boolean Plugin_248(byte function, struct EventStruct *event, String& string) 22 | { 23 | boolean success = false; 24 | 25 | switch (function) 26 | { 27 | case PLUGIN_DEVICE_ADD: 28 | { 29 | Device[++deviceCount].Number = PLUGIN_ID_248; 30 | Device[deviceCount].Type = DEVICE_TYPE_I2C; 31 | Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_TEMP_HUM; 32 | Device[deviceCount].Ports = 0; 33 | Device[deviceCount].PullUpOption = false; 34 | Device[deviceCount].InverseLogicOption = false; 35 | Device[deviceCount].FormulaOption = true; 36 | Device[deviceCount].ValueCount = 2; 37 | Device[deviceCount].SendDataOption = true; 38 | Device[deviceCount].TimerOption = true; 39 | Device[deviceCount].GlobalSyncOption = true; 40 | break; 41 | } 42 | 43 | case PLUGIN_GET_DEVICENAME: 44 | { 45 | string = F(PLUGIN_NAME_248); 46 | break; 47 | } 48 | 49 | case PLUGIN_GET_DEVICEVALUENAMES: 50 | { 51 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_248)); 52 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_248)); 53 | break; 54 | } 55 | 56 | case PLUGIN_INIT: 57 | { 58 | initPluginTaskData(event->TaskIndex, 59 | new (std::nothrow) P248_data_struct(PCONFIG(0), (AHTx_device_type)PCONFIG(1))); 60 | P248_data_struct *P248_data = 61 | static_cast(getPluginTaskData(event->TaskIndex)); 62 | 63 | if (nullptr == P248_data) { 64 | return success; 65 | } 66 | success = true; 67 | 68 | break; 69 | } 70 | 71 | case PLUGIN_WEBFORM_SHOW_I2C_PARAMS: 72 | { 73 | int Plugin_28_i2c_addresses[2] = { 0x38, 0x39 }; 74 | addFormSelectorI2C(F("i2c_addr"), 2, Plugin_28_i2c_addresses, PCONFIG(0)); 75 | break; 76 | } 77 | 78 | case PLUGIN_WEBFORM_LOAD: 79 | { 80 | addFormNote(F("SDO Low=0x38, High=0x39")); 81 | 82 | const String options[] = { F("AHT10"), F("AHT20"), F("AHT21") }; 83 | int indices[] = { AHT10_DEVICE, AHT20_DEVICE, AHT21_DEVICE }; 84 | addFormSelector(F("Sensor model"), F("p248_ahttype"), 3, options, indices, PCONFIG(1)); 85 | 86 | success = true; 87 | break; 88 | } 89 | 90 | case PLUGIN_WEBFORM_SAVE: 91 | { 92 | PCONFIG(0) = getFormItemInt(F("i2c_addr")); 93 | PCONFIG(1) = getFormItemInt(F("p248_ahttype")); 94 | success = true; 95 | break; 96 | } 97 | case PLUGIN_ONCE_A_SECOND: 98 | { 99 | P248_data_struct *P248_data = 100 | static_cast(getPluginTaskData(event->TaskIndex)); 101 | 102 | if (nullptr != P248_data) { 103 | if (P248_data->updateMeasurements(event->TaskIndex)) { 104 | // Update was succesfull, schedule a read. 105 | Scheduler.schedule_task_device_timer(event->TaskIndex, millis() + 10); 106 | } 107 | } 108 | break; 109 | } 110 | 111 | case PLUGIN_READ: 112 | { 113 | P248_data_struct *P248_data = 114 | static_cast(getPluginTaskData(event->TaskIndex)); 115 | 116 | if (nullptr != P248_data) { 117 | if (P248_data->state != AHTx_New_values) { 118 | success = false; 119 | break; 120 | } 121 | P248_data->state = AHTx_Values_read; 122 | 123 | UserVar[event->BaseVarIndex] = P248_data->getTemperature(); 124 | UserVar[event->BaseVarIndex + 1] = P248_data->getHumidity(); 125 | 126 | if (loglevelActiveFor(LOG_LEVEL_INFO)) { 127 | String log; 128 | log.reserve(40); // Prevent re-allocation 129 | log = P248_data->getDeviceName(); 130 | log += F(" : Address: 0x"); 131 | log += String(PCONFIG(0), HEX); 132 | addLog(LOG_LEVEL_INFO, log); 133 | log = P248_data->getDeviceName(); 134 | log += F(" : Temperature: "); 135 | log += formatUserVarNoCheck(event->TaskIndex, 0); 136 | addLog(LOG_LEVEL_INFO, log); 137 | 138 | log = P248_data->getDeviceName(); 139 | log += F(" : Humidity: "); 140 | log += formatUserVarNoCheck(event->TaskIndex, 1); 141 | addLog(LOG_LEVEL_INFO, log); 142 | } 143 | success = true; 144 | } 145 | break; 146 | } 147 | } 148 | return success; 149 | } 150 | 151 | #endif // USES_P248 152 | -------------------------------------------------------------------------------- /_P248_TempHumidity_AHT1x.ino: -------------------------------------------------------------------------------- 1 | //####################################################################################################### 2 | //################ Plugin 248: AHT10 Temperature and Humidity Sensor (I2C) ################### 3 | //####################################################################################################### 4 | /* AHT10/15/20 - Temperature and Humidity 5 | * 6 | * AHT1x I2C Address: 0x38, 0x39 7 | * the driver supports two I2c adresses but only one Sensor allowed. 8 | * 9 | * ATTENTION: The AHT10/15 Sensor is incompatible with other I2C devices on I2C bus. 10 | * 11 | * The Datasheet write: 12 | * "Only a single AHT10 can be connected to the I2C bus and no other I2C 13 | * devices can be connected". 14 | * 15 | * after lot of search and tests, now is confirmed that works only reliable with one sensor 16 | * on I2C Bus 17 | */ 18 | //####################################################################################################### 19 | 20 | #ifdef USES_P248 21 | 22 | #include "_Plugin_Helper.h" 23 | 24 | #include "src/PluginStructs/P248_data_struct.h" 25 | 26 | 27 | //#ifdef PLUGIN_BUILD_TESTING 28 | 29 | #define PLUGIN_248 30 | #define PLUGIN_ID_248 248 31 | #define PLUGIN_NAME_248 "Environment - AHT10 [TESTING]" 32 | #define PLUGIN_VALUENAME1_248 "Temperature" 33 | #define PLUGIN_VALUENAME2_248 "Humidity" 34 | 35 | 36 | 37 | //============================================== 38 | // PLUGIN 39 | // ============================================= 40 | 41 | boolean Plugin_248(byte function, struct EventStruct *event, String& string) 42 | { 43 | boolean success = false; 44 | 45 | switch (function) 46 | { 47 | case PLUGIN_DEVICE_ADD: 48 | { 49 | Device[++deviceCount].Number = PLUGIN_ID_248; 50 | Device[deviceCount].Type = DEVICE_TYPE_I2C; 51 | Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_TEMP_HUM; 52 | Device[deviceCount].Ports = 0; 53 | Device[deviceCount].PullUpOption = false; 54 | Device[deviceCount].InverseLogicOption = false; 55 | Device[deviceCount].FormulaOption = true; 56 | Device[deviceCount].ValueCount = 2; 57 | Device[deviceCount].SendDataOption = true; 58 | Device[deviceCount].TimerOption = true; 59 | Device[deviceCount].GlobalSyncOption = true; 60 | break; 61 | } 62 | 63 | case PLUGIN_GET_DEVICENAME: 64 | { 65 | string = F(PLUGIN_NAME_248); 66 | break; 67 | } 68 | 69 | case PLUGIN_GET_DEVICEVALUENAMES: 70 | { 71 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_248)); 72 | strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_248)); 73 | break; 74 | } 75 | 76 | case PLUGIN_WEBFORM_SHOW_I2C_PARAMS: 77 | { 78 | int optionValues[2] = { 0x38, 0x39 }; 79 | addFormSelectorI2C(F("i2c_addr"), 2, optionValues, PCONFIG(0)); 80 | addFormNote(F("AO Low=0x38, High=0x39")); 81 | break; 82 | } 83 | 84 | case PLUGIN_WEBFORM_LOAD: 85 | { 86 | success = true; 87 | break; 88 | } 89 | 90 | case PLUGIN_WEBFORM_SAVE: 91 | { 92 | PCONFIG(0) = getFormItemInt(F("i2c_addr")); 93 | 94 | success = true; 95 | break; 96 | } 97 | 98 | case PLUGIN_INIT: 99 | { 100 | initPluginTaskData(event->TaskIndex, new (std::nothrow) P248_data_struct(PCONFIG(0))); 101 | P248_data_struct *P248_data = static_cast(getPluginTaskData(event->TaskIndex)); 102 | 103 | if (nullptr == P248_data) { 104 | return success; 105 | } 106 | 107 | success = true; 108 | break; 109 | } 110 | case PLUGIN_READ: 111 | { 112 | P248_data_struct *P248_data = static_cast(getPluginTaskData(event->TaskIndex)); 113 | 114 | if (nullptr != P248_data) { 115 | if (P248_data->state != New_values) { 116 | P248_data->update(event->TaskIndex); //run the state machine 117 | Scheduler.schedule_task_device_timer(event->TaskIndex, millis() + AHT10_MEASURMENT_DELAY); 118 | success = false; 119 | break; 120 | } 121 | P248_data->state = Values_read; 122 | 123 | 124 | UserVar[event->BaseVarIndex] = P248_data->last_temp_val; 125 | UserVar[event->BaseVarIndex + 1] = P248_data->last_hum_val; 126 | 127 | if (loglevelActiveFor(LOG_LEVEL_INFO)) { 128 | String log = F("AHT1x: Temperature: "); 129 | log += UserVar[event->BaseVarIndex + 0]; 130 | addLog(LOG_LEVEL_INFO, log); 131 | log = F("AHT1x: Humidity: "); 132 | log += UserVar[event->BaseVarIndex + 1]; 133 | addLog(LOG_LEVEL_INFO, log); 134 | } 135 | 136 | success = true; 137 | break; 138 | } 139 | } 140 | } 141 | return success; 142 | } 143 | 144 | //#endif // testing 145 | 146 | #endif //USES_P248 -------------------------------------------------------------------------------- /esp8266-oled-ssd1306.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letscontrolit/ESPEasyPluginPlayground/68099a890b7fdbd137f264eb013e0db6e9ebbbe0/esp8266-oled-ssd1306.zip -------------------------------------------------------------------------------- /libraries _PLUGIN145 ITHO FAN/Itho/CC1101.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Klusjesman, modified bij supersjimmie for Arduino/ESP8266 3 | */ 4 | 5 | #include "CC1101.h" 6 | 7 | // default constructor 8 | CC1101::CC1101() 9 | { 10 | SPI.begin(); 11 | #ifdef ESP8266 12 | pinMode(SS, OUTPUT); 13 | #endif 14 | } //CC1101 15 | 16 | // default destructor 17 | CC1101::~CC1101() 18 | { 19 | } //~CC1101 20 | 21 | /***********************/ 22 | // SPI helper functions select() and deselect() 23 | inline void CC1101::select(void) { 24 | digitalWrite(SS, LOW); 25 | } 26 | 27 | inline void CC1101::deselect(void) { 28 | digitalWrite(SS, HIGH); 29 | } 30 | 31 | void CC1101::spi_waitMiso() 32 | { 33 | while(digitalRead(MISO) == HIGH) yield(); 34 | } 35 | 36 | void CC1101::init() 37 | { 38 | reset(); 39 | } 40 | 41 | void CC1101::reset() 42 | { 43 | deselect(); 44 | delayMicroseconds(5); 45 | select(); 46 | delayMicroseconds(10); 47 | deselect(); 48 | delayMicroseconds(45); 49 | select(); 50 | 51 | spi_waitMiso(); 52 | SPI.transfer(CC1101_SRES); 53 | delay(10); 54 | spi_waitMiso(); 55 | deselect(); 56 | } 57 | 58 | uint8_t CC1101::writeCommand(uint8_t command) 59 | { 60 | uint8_t result; 61 | 62 | select(); 63 | spi_waitMiso(); 64 | result = SPI.transfer(command); 65 | deselect(); 66 | 67 | return result; 68 | } 69 | 70 | void CC1101::writeRegister(uint8_t address, uint8_t data) 71 | { 72 | select(); 73 | spi_waitMiso(); 74 | SPI.transfer(address); 75 | SPI.transfer(data); 76 | deselect(); 77 | } 78 | 79 | uint8_t CC1101::readRegister(uint8_t address) 80 | { 81 | uint8_t val; 82 | 83 | select(); 84 | spi_waitMiso(); 85 | SPI.transfer(address); 86 | val = SPI.transfer(0); 87 | deselect(); 88 | 89 | return val; 90 | } 91 | 92 | uint8_t CC1101::readRegisterMedian3(uint8_t address) 93 | { 94 | uint8_t val, val1, val2, val3; 95 | 96 | select(); 97 | spi_waitMiso(); 98 | SPI.transfer(address); 99 | val1 = SPI.transfer(0); 100 | SPI.transfer(address); 101 | val2 = SPI.transfer(0); 102 | SPI.transfer(address); 103 | val3 = SPI.transfer(0); 104 | deselect(); 105 | // reverse sort (largest in val1) because this is te expected order for TX_BUFFER 106 | if (val3 > val2) {val = val3; val3 = val2; val2 = val; } //Swap(val3,val2) 107 | if (val2 > val1) {val = val2; val2 = val1, val1 = val; } //Swap(val2,val1) 108 | if (val3 > val2) {val = val3; val3 = val2, val2 = val; } //Swap(val3,val2) 109 | 110 | return val2; 111 | } 112 | 113 | /* Known SPI/26MHz synchronization bug (see CC1101 errata) 114 | This issue affects the following registers: SPI status byte (fields STATE and FIFO_BYTES_AVAILABLE), 115 | FREQEST or RSSI while the receiver is active, MARCSTATE at any time other than an IDLE radio state, 116 | RXBYTES when receiving or TXBYTES when transmitting, and WORTIME1/WORTIME0 at any time.*/ 117 | //uint8_t CC1101::readRegisterWithSyncProblem(uint8_t address, uint8_t registerType) 118 | uint8_t /* ICACHE_RAM_ATTR */ CC1101::readRegisterWithSyncProblem(uint8_t address, uint8_t registerType) 119 | { 120 | uint8_t value1, value2; 121 | 122 | value1 = readRegister(address | registerType); 123 | 124 | //if two consecutive reads gives us the same result then we know we are ok 125 | do 126 | { 127 | value2 = value1; 128 | value1 = readRegister(address | registerType); 129 | } 130 | while (value1 != value2); 131 | 132 | return value1; 133 | } 134 | 135 | //registerType = CC1101_CONFIG_REGISTER or CC1101_STATUS_REGISTER 136 | uint8_t CC1101::readRegister(uint8_t address, uint8_t registerType) 137 | { 138 | switch (address) 139 | { 140 | case CC1101_FREQEST: 141 | case CC1101_MARCSTATE: 142 | case CC1101_RXBYTES: 143 | case CC1101_TXBYTES: 144 | case CC1101_WORTIME1: 145 | case CC1101_WORTIME0: 146 | return readRegisterWithSyncProblem(address, registerType); 147 | 148 | default: 149 | return readRegister(address | registerType); 150 | } 151 | } 152 | 153 | void CC1101::writeBurstRegister(uint8_t address, uint8_t* data, uint8_t length) 154 | { 155 | uint8_t i; 156 | 157 | select(); 158 | spi_waitMiso(); 159 | SPI.transfer(address | CC1101_WRITE_BURST); 160 | for (i = 0; i < length; i++) { 161 | SPI.transfer(data[i]); 162 | } 163 | deselect(); 164 | } 165 | 166 | void CC1101::readBurstRegister(uint8_t* buffer, uint8_t address, uint8_t length) 167 | { 168 | uint8_t i; 169 | 170 | select(); 171 | spi_waitMiso(); 172 | SPI.transfer(address | CC1101_READ_BURST); 173 | 174 | for (i = 0; i < length; i++) { 175 | buffer[i] = SPI.transfer(0x00); 176 | } 177 | 178 | deselect(); 179 | } 180 | 181 | //wait for fixed length in rx fifo 182 | uint8_t CC1101::receiveData(CC1101Packet* packet, uint8_t length) 183 | { 184 | uint8_t rxBytes = readRegisterWithSyncProblem(CC1101_RXBYTES, CC1101_STATUS_REGISTER); 185 | rxBytes = rxBytes & CC1101_BITS_RX_BYTES_IN_FIFO; 186 | 187 | //check for rx fifo overflow 188 | if ((readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER) & CC1101_BITS_MARCSTATE) == CC1101_MARCSTATE_RXFIFO_OVERFLOW) 189 | { 190 | writeCommand(CC1101_SIDLE); //idle 191 | writeCommand(CC1101_SFRX); //flush RX buffer 192 | writeCommand(CC1101_SRX); //switch to RX state 193 | } 194 | else if (rxBytes == length) 195 | { 196 | readBurstRegister(packet->data, CC1101_RXFIFO, rxBytes); 197 | 198 | //continue RX 199 | writeCommand(CC1101_SIDLE); //idle 200 | writeCommand(CC1101_SFRX); //flush RX buffer 201 | writeCommand(CC1101_SRX); //switch to RX state 202 | 203 | packet->length = rxBytes; 204 | } 205 | else 206 | { 207 | //empty fifo 208 | packet->length = 0; 209 | writeCommand(CC1101_SIDLE); //idle 210 | writeCommand(CC1101_SFRX); //flush RX buffer 211 | writeCommand(CC1101_SRX); //switch to RX state 212 | } 213 | 214 | return packet->length; 215 | } 216 | 217 | //This function is able to send packets bigger then the FIFO size. 218 | void CC1101::sendData(CC1101Packet *packet) 219 | { 220 | uint8_t index = 0; 221 | uint8_t txStatus, MarcState; 222 | uint8_t length; 223 | 224 | writeCommand(CC1101_SIDLE); //idle 225 | 226 | txStatus = readRegisterWithSyncProblem(CC1101_TXBYTES, CC1101_STATUS_REGISTER); 227 | 228 | //clear TX fifo if needed 229 | if (txStatus & CC1101_BITS_TX_FIFO_UNDERFLOW) 230 | { 231 | writeCommand(CC1101_SIDLE); //idle 232 | writeCommand(CC1101_SFTX); //flush TX buffer 233 | } 234 | 235 | writeCommand(CC1101_SIDLE); //idle 236 | 237 | //determine how many bytes to send 238 | length = (packet->length <= CC1101_DATA_LEN ? packet->length : CC1101_DATA_LEN); 239 | 240 | writeBurstRegister(CC1101_TXFIFO, packet->data, length); 241 | 242 | writeCommand(CC1101_SIDLE); 243 | //start sending packet 244 | writeCommand(CC1101_STX); 245 | 246 | //continue sending when packet is bigger than 64 bytes 247 | if (packet->length > CC1101_DATA_LEN) 248 | { 249 | index += length; 250 | 251 | //loop until all bytes are transmitted 252 | while (index < packet->length) 253 | { 254 | //check if there is free space in the fifo 255 | while ((txStatus = (readRegisterMedian3(CC1101_TXBYTES | CC1101_STATUS_REGISTER) & CC1101_BITS_RX_BYTES_IN_FIFO)) > (CC1101_DATA_LEN - 2)); 256 | 257 | //calculate how many bytes we can send 258 | length = (CC1101_DATA_LEN - txStatus); 259 | length = ((packet->length - index) < length ? (packet->length - index) : length); 260 | 261 | //send some more bytes 262 | for (int i=0; idata[index+i]); 264 | 265 | index += length; 266 | } 267 | } 268 | 269 | //wait until transmission is finished (TXOFF_MODE is expected to be set to 0/IDLE or TXFIFO_UNDERFLOW) 270 | do 271 | { 272 | MarcState = (readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER) & CC1101_BITS_MARCSTATE); 273 | // if (MarcState == CC1101_MARCSTATE_TXFIFO_UNDERFLOW) Serial.print(F("TXFIFO_UNDERFLOW occured in sendData() \n")); 274 | } 275 | while((MarcState != CC1101_MARCSTATE_IDLE) && (MarcState != CC1101_MARCSTATE_TXFIFO_UNDERFLOW)); 276 | } 277 | -------------------------------------------------------------------------------- /libraries _PLUGIN145 ITHO FAN/Itho/CC1101Packet.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Klusjesman, modified bij supersjimmie for Arduino/ESP8266 3 | */ 4 | 5 | #ifndef CC1101PACKET_H_ 6 | #define CC1101PACKET_H_ 7 | 8 | #include 9 | #include 10 | 11 | #define CC1101_BUFFER_LEN 64 12 | #define CC1101_DATA_LEN CC1101_BUFFER_LEN - 3 13 | 14 | 15 | class CC1101Packet 16 | { 17 | public: 18 | uint8_t length; 19 | uint8_t data[128]; 20 | }; 21 | 22 | 23 | #endif /* CC1101PACKET_H_ */ 24 | -------------------------------------------------------------------------------- /libraries _PLUGIN145 ITHO FAN/Itho/IthoCC1101.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Klusjesman, supersjimmie, modified and reworked by arjenhiemstra 3 | */ 4 | 5 | #ifndef __ITHOCC1101_H__ 6 | #define __ITHOCC1101_H__ 7 | 8 | #include 9 | #include "CC1101.h" 10 | #include "IthoPacket.h" 11 | 12 | 13 | //pa table settings 14 | const uint8_t ithoPaTableSend[8] = {0x6F, 0x26, 0x2E, 0x8C, 0x87, 0xCD, 0xC7, 0xC0}; 15 | const uint8_t ithoPaTableReceive[8] = {0x6F, 0x26, 0x2E, 0x7F, 0x8A, 0x84, 0xCA, 0xC4}; 16 | 17 | //message command bytes 18 | const uint8_t ithoMessageRVHighCommandBytes[] = {49,224,4,0,0,200}; 19 | const uint8_t ithoMessageHighCommandBytes[] = {34,241,3,0,4,4}; 20 | const uint8_t ithoMessageFullCommandBytes[] = {34,241,3,0,4,4}; 21 | const uint8_t ithoMessageMediumCommandBytes[] = {34,241,3,0,3,4}; 22 | const uint8_t ithoMessageRVMediumCommandBytes[] = {34,241,3,0,3,7}; 23 | const uint8_t ithoMessageLowCommandBytes[] = {34,241,3,0,2,4}; 24 | const uint8_t ithoMessageRVLowCommandBytes[] = {49,224,4,0,0,1}; 25 | const uint8_t ithoMessageRVAutoCommandBytes[] = {34,241,3,0,5,7}; 26 | const uint8_t ithoMessageStandByCommandBytes[] = {0,0,0,0,0,0}; //unkown, tbd 27 | const uint8_t ithoMessageTimer1CommandBytes[] = {34,243,3,0,0,10}; //10 minutes full speed 28 | const uint8_t ithoMessageTimer2CommandBytes[] = {34,243,3,0,0,20}; //20 minutes full speed 29 | const uint8_t ithoMessageTimer3CommandBytes[] = {34,243,3,0,0,30}; //30 minutes full speed 30 | const uint8_t ithoMessageJoinCommandBytes[] = {31,201,12,0,34,241}; 31 | const uint8_t ithoMessageJoin2CommandBytes[] = {31,201,12,99,34,248}; //join command of RFT AUTO Co2 remote 32 | const uint8_t ithoMessageRVJoinCommandBytes[] = {31,201,24,0,49,224}; //join command of RFT-RV 33 | const uint8_t ithoMessageLeaveCommandBytes[] = {31,201,6,0,31,201}; 34 | //itho rft-rv 35 | //unknown, high 36 | //148,216,43,49,224,4,0,0,200,0,3,127,244,78,11,155,154,225,11,96,138 37 | //148,216,43,49,224,4,0,0,200,0,3,127,51,80,47,233,94,6,189,114,73 38 | 39 | //low 40 | //148,216,43,49,224,4,0,0,1,0,202,127,242,212,160,123,15,64,7,129,33 41 | //148,216,43,34,241,3,0,4,4,194,127,255,189,90,107,88,72,115,49,192,105 42 | 43 | //join 44 | //151,149,65,31,201,24,0,49,224,151,149,65,0,18,160,151,149,65,1,16,224 45 | 46 | 47 | class IthoCC1101 : protected CC1101 48 | { 49 | private: 50 | //receive 51 | CC1101Packet inMessage; //temp storage message2 52 | IthoPacket inIthoPacket; //stores last received message data 53 | 54 | //send 55 | IthoPacket outIthoPacket; //stores state of "remote" 56 | 57 | //settings 58 | uint8_t sendTries; //number of times a command is send at one button press 59 | 60 | //functions 61 | public: 62 | IthoCC1101(uint8_t counter = 0, uint8_t sendTries = 3); //set initial counter value 63 | ~IthoCC1101(); 64 | 65 | //init 66 | void init() { CC1101::init(); initReceive(); } //init,reset CC1101 67 | void initReceive(); 68 | uint8_t getLastCounter() { return outIthoPacket.counter; } //counter is increased before sending a command 69 | void setSendTries(uint8_t sendTries) { this->sendTries = sendTries; } 70 | void setDeviceID(uint8_t byte0, uint8_t byte1, uint8_t byte2) { this->outIthoPacket.deviceId[0] = byte0; this->outIthoPacket.deviceId[1] = byte1; this->outIthoPacket.deviceId[2] = byte2;} 71 | 72 | //receive 73 | bool checkForNewPacket(); //check RX fifo for new data 74 | IthoPacket getLastPacket() { return inIthoPacket; } //retrieve last received/parsed packet from remote 75 | IthoCommand getLastCommand() { return inIthoPacket.command; } //retrieve last received/parsed command from remote 76 | uint8_t getLastInCounter() { return inIthoPacket.counter; } //retrieve last received/parsed command from remote 77 | uint8_t ReadRSSI(); 78 | bool checkID(const uint8_t *id); 79 | int * getLastID(); 80 | String getLastIDstr(bool ashex=true); 81 | String getLastMessagestr(bool ashex=true); 82 | String LastMessageDecoded(); 83 | 84 | //send 85 | void sendCommand(IthoCommand command); 86 | protected: 87 | private: 88 | IthoCC1101( const IthoCC1101 &c); 89 | IthoCC1101& operator=( const IthoCC1101 &c); 90 | 91 | //init CC1101 for receiving 92 | void initReceiveMessage(); 93 | 94 | //init CC1101 for sending 95 | void initSendMessage(uint8_t len); 96 | void finishTransfer(); 97 | 98 | //parse received message 99 | bool parseMessageCommand(); 100 | bool checkIthoCommand(IthoPacket *itho, const uint8_t commandBytes[]); 101 | 102 | //send 103 | void createMessageStart(IthoPacket *itho, CC1101Packet *packet); 104 | void createMessageCommand(IthoPacket *itho, CC1101Packet *packet); 105 | void createMessageJoin(IthoPacket *itho, CC1101Packet *packet); 106 | void createMessageLeave(IthoPacket *itho, CC1101Packet *packet); 107 | uint8_t* getMessageCommandBytes(IthoCommand command); 108 | uint8_t getCounter2(IthoPacket *itho, uint8_t len); 109 | 110 | uint8_t messageEncode(IthoPacket *itho, CC1101Packet *packet); 111 | void messageDecode(CC1101Packet *packet, IthoPacket *itho); 112 | 113 | 114 | }; //IthoCC1101 115 | 116 | #endif //__ITHOCC1101_H__ -------------------------------------------------------------------------------- /libraries _PLUGIN145 ITHO FAN/Itho/IthoPacket.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Klusjesman, supersjimmie, modified and reworked by arjenhiemstra 3 | */ 4 | 5 | #ifndef ITHOPACKET_H_ 6 | #define ITHOPACKET_H_ 7 | 8 | enum IthoCommand 9 | { 10 | IthoUnknown = 0, 11 | 12 | IthoJoin = 1, 13 | IthoLeave = 2, 14 | 15 | IthoStandby = 3, 16 | IthoLow = 4, 17 | IthoMedium = 5, 18 | IthoHigh = 6, 19 | IthoFull = 7, 20 | 21 | IthoTimer1 = 8, 22 | IthoTimer2 = 9, 23 | IthoTimer3 = 10, 24 | 25 | //duco c system remote 26 | DucoStandby = 11, 27 | DucoLow = 12, 28 | DucoMedium = 13, 29 | DucoHigh = 14 30 | }; 31 | 32 | 33 | class IthoPacket 34 | { 35 | public: 36 | IthoCommand command; 37 | 38 | uint8_t dataDecoded[32]; 39 | uint8_t dataDecodedChk[32]; 40 | uint8_t length; 41 | 42 | uint8_t deviceType; 43 | uint8_t deviceId[3]; 44 | 45 | uint8_t counter; //0-255, counter is increased on every remote button press 46 | }; 47 | 48 | 49 | #endif /* ITHOPACKET_H_ */ -------------------------------------------------------------------------------- /src/PluginStructs/P248_data_struct.cpp: -------------------------------------------------------------------------------- 1 | #include "../PluginStructs/P248_data_struct.h" 2 | 3 | #ifdef USES_P248 4 | 5 | # include "../Helpers/Convert.h" 6 | 7 | struct AHTx_Status { 8 | inline AHTx_Status(uint8_t stat) : status(stat) {} 9 | 10 | inline bool valid() const { return status != 0xFF; } 11 | 12 | inline bool calibrated() const { return (status & (1 << 3)) != 0; } 13 | 14 | inline bool busy() const { return (status & (1 << 7)) != 0; } 15 | 16 | const uint8_t status; 17 | }; 18 | 19 | AHTx_Device::AHTx_Device(uint8_t addr, AHTx_device_type type) : 20 | i2cAddress(addr), 21 | device_type(type), 22 | last_hum_val(0), 23 | last_temp_val(0) {} 24 | 25 | String AHTx_Device::getDeviceName() const { 26 | switch(device_type) { 27 | case AHT10_DEVICE: return F("AHT10"); 28 | case AHT20_DEVICE: return F("AHT20"); 29 | case AHT21_DEVICE: return F("AHT21"); 30 | default: return F("AHTx"); 31 | } 32 | } 33 | 34 | bool AHTx_Device::initialize() { 35 | const uint8_t cmd_init = (device_type == AHT10_DEVICE) ? 0xE1 : 0xBE; 36 | return I2C_write16_reg(i2cAddress, cmd_init, 0x0800); 37 | } 38 | 39 | bool AHTx_Device::triggerMeasurement() { 40 | return I2C_write16_reg(i2cAddress, 0xAC, 0x3300); // measurement time takes over 80 msec 41 | } 42 | 43 | bool AHTx_Device::softReset() { 44 | return I2C_write8(i2cAddress, 0xBA); // soft reset takes less than 20 msec 45 | } 46 | 47 | uint8_t AHTx_Device::readStatus() { 48 | return I2C_read8(i2cAddress, NULL); 49 | } 50 | 51 | bool AHTx_Device::readData() { 52 | const uint8_t data_len = 6; 53 | 54 | // I2C_read8 len 55 | if (Wire.requestFrom(i2cAddress, data_len) == 0) { 56 | return false; 57 | } 58 | 59 | uint8_t data[data_len]; 60 | for (uint8_t i = 0; i < data_len; ++i) { 61 | data[i] = Wire.read(); 62 | } 63 | 64 | // check status 65 | AHTx_Status status = data[0]; 66 | if (not (status.valid() and status.calibrated())) { 67 | return false; 68 | } 69 | 70 | // 20 bits humidity value 71 | uint32_t value = data[1]; 72 | value = (value << 8) | data[2]; 73 | value = (value << 4) | (data[3] >> 4); 74 | last_hum_val = (float)value / (1 << 20) * 100; 75 | 76 | // 20 bits temperature value 77 | value = data[3] & 0x0F; 78 | value = (value << 8) | data[4]; 79 | value = (value << 8) | data[5]; 80 | last_temp_val = (float)value / (1 << 20) * 200 - 50; 81 | 82 | return true; 83 | } 84 | 85 | P248_data_struct::P248_data_struct(uint8_t addr, AHTx_device_type dev) : 86 | device(addr, dev), 87 | state(AHTx_Uninitialized), 88 | last_measurement(0), 89 | trigger_time(0) {} 90 | 91 | bool P248_data_struct::initialized() const { 92 | return state != AHTx_Uninitialized; 93 | } 94 | 95 | void P248_data_struct::setUninitialized() { 96 | state = AHTx_Uninitialized; 97 | } 98 | 99 | // Perform the measurements with interval 100 | bool P248_data_struct::updateMeasurements(unsigned long task_index) { 101 | const unsigned long current_time = millis(); 102 | 103 | if (!initialized()) { 104 | if (!device.initialize()) { 105 | addLog(LOG_LEVEL_DEBUG, getDeviceName() + F(" : unable to initialize")); 106 | return false; 107 | } 108 | addLog(LOG_LEVEL_INFO, getDeviceName() + F(" : initialized")); 109 | 110 | trigger_time = current_time; 111 | state = AHTx_Trigger_measurement; 112 | return false; 113 | } 114 | 115 | if (state != AHTx_Wait_for_samples and state != AHTx_Trigger_measurement) { 116 | if (!timeOutReached(last_measurement + (Settings.TaskDeviceTimer[task_index] * 1000))) { 117 | // Timeout has not yet been reached. 118 | return false; 119 | } 120 | trigger_time = current_time; 121 | state = AHTx_Trigger_measurement; 122 | } 123 | 124 | // state: AHTx_Wait_for_samples or AHTx_Trigger_measurement 125 | AHTx_Status status = device.readStatus(); 126 | if (status.valid() and status.calibrated() and not status.busy()) { 127 | 128 | if (state == AHTx_Trigger_measurement) { 129 | device.triggerMeasurement(); 130 | 131 | trigger_time = current_time; 132 | state = AHTx_Wait_for_samples; 133 | return false; 134 | } 135 | 136 | // state: AHTx_Wait_for_samples 137 | if (!device.readData()) { 138 | return false; 139 | } 140 | 141 | last_measurement = current_time; 142 | state = AHTx_New_values; 143 | 144 | if (loglevelActiveFor(LOG_LEVEL_INFO)) { 145 | String log; 146 | log.reserve(120); // Prevent re-allocation 147 | log = getDeviceName(); 148 | log += F(" : humidity "); 149 | log += device.getHumidity(); 150 | log += F("% temperature "); 151 | log += device.getTemperature(); 152 | log += F("C"); 153 | addLog(LOG_LEVEL_INFO, log); 154 | } 155 | 156 | return true; 157 | } 158 | 159 | if (timePassedSince(trigger_time) > 1000) { 160 | // should not happen 161 | addLog(LOG_LEVEL_ERROR, getDeviceName() + F(" : reset")); 162 | device.softReset(); 163 | 164 | state = AHTx_Uninitialized; 165 | } 166 | 167 | return false; 168 | } 169 | 170 | #endif // ifdef USES_P248 171 | -------------------------------------------------------------------------------- /src/PluginStructs/P248_data_struct.h: -------------------------------------------------------------------------------- 1 | #ifndef PLUGINSTRUCTS_P248_DATA_STRUCT_H 2 | #define PLUGINSTRUCTS_P248_DATA_STRUCT_H 3 | 4 | #include "../../_Plugin_Helper.h" 5 | #ifdef USES_P248 6 | 7 | enum AHTx_device_type { 8 | AHT10_DEVICE = 10, 9 | AHT20_DEVICE = 20, 10 | AHT21_DEVICE = 21, 11 | }; 12 | 13 | enum AHTx_state { 14 | AHTx_Uninitialized = 0, 15 | AHTx_Initialized, 16 | AHTx_Trigger_measurement, 17 | AHTx_Wait_for_samples, 18 | AHTx_New_values, 19 | AHTx_Values_read 20 | }; 21 | 22 | class AHTx_Device { 23 | public: 24 | AHTx_Device(uint8_t addr, AHTx_device_type type); 25 | 26 | String getDeviceName() const; 27 | inline float getHumidity() const { return last_hum_val; } 28 | inline float getTemperature() const { return last_temp_val; } 29 | 30 | bool initialize(); 31 | bool triggerMeasurement(); 32 | bool softReset(); 33 | uint8_t readStatus(); 34 | bool readData(); 35 | 36 | protected: 37 | const uint8_t i2cAddress; 38 | const AHTx_device_type device_type; 39 | float last_hum_val; 40 | float last_temp_val; 41 | }; 42 | 43 | struct P248_data_struct : public PluginTaskData_base { 44 | P248_data_struct(uint8_t addr, AHTx_device_type dev); 45 | 46 | inline String getDeviceName() const { return device.getDeviceName(); } 47 | inline float getHumidity() const { return device.getHumidity(); } 48 | inline float getTemperature() const { return device.getTemperature(); } 49 | 50 | bool initialized() const; 51 | 52 | void setUninitialized(); 53 | 54 | // Perform the measurements with interval 55 | bool updateMeasurements(unsigned long task_index); 56 | 57 | AHTx_Device device; 58 | AHTx_state state; 59 | unsigned long last_measurement; 60 | unsigned long trigger_time; 61 | }; 62 | 63 | #endif // ifdef USES_P248 64 | 65 | #endif // PLUGINSTRUCTS_P248_DATA_STRUCT_H 66 | --------------------------------------------------------------------------------