// ----------------------------------------------------------------------------- // V9261F based power monitor // Copyright (C) 2017-2019 by Xose PĂ©rez // ----------------------------------------------------------------------------- #if SENSOR_SUPPORT && V9261F_SUPPORT #pragma once #include #include #include "BaseEmonSensor.h" extern "C" { #include "../libs/fs_math.h" } class V9261FSensor : public BaseEmonSensor { public: // --------------------------------------------------------------------- // Public // --------------------------------------------------------------------- V9261FSensor(): _data() { _count = 6; _sensor_id = SENSOR_V9261F_ID; } ~V9261FSensor() { if (_serial) delete _serial; } // --------------------------------------------------------------------- void setRX(unsigned char pin_rx) { if (_pin_rx == pin_rx) return; _pin_rx = pin_rx; _dirty = true; } void setInverted(bool inverted) { if (_inverted == inverted) return; _inverted = inverted; _dirty = true; } // --------------------------------------------------------------------- unsigned char getRX() { return _pin_rx; } bool getInverted() { return _inverted; } // --------------------------------------------------------------------- // Sensor API // --------------------------------------------------------------------- // Initialization method, must be idempotent void begin() { if (!_dirty) return; if (_serial) delete _serial; _serial = new SoftwareSerial(_pin_rx, -1, _inverted); _serial->enableIntTx(false); _serial->begin(V9261F_BAUDRATE); _ready = true; _dirty = false; } // Descriptive name of the sensor String description() { char buffer[28]; snprintf(buffer, sizeof(buffer), "V9261F @ SwSerial(%u,NULL)", _pin_rx); return String(buffer); } // Descriptive name of the slot # index String description(unsigned char index) { return description(); }; // Address of the sensor (it could be the GPIO or I2C address) String address(unsigned char index) { return String(_pin_rx); } // Loop-like method, call it in your main loop void tick() { _read(); } // Type for slot # index unsigned char type(unsigned char index) { if (index == 0) return MAGNITUDE_CURRENT; if (index == 1) return MAGNITUDE_VOLTAGE; if (index == 2) return MAGNITUDE_POWER_ACTIVE; if (index == 3) return MAGNITUDE_POWER_REACTIVE; if (index == 4) return MAGNITUDE_POWER_APPARENT; if (index == 5) return MAGNITUDE_POWER_FACTOR; if (index == 6) return MAGNITUDE_ENERGY; return MAGNITUDE_NONE; } // Current value for slot # index double value(unsigned char index) { if (index == 0) return _current; if (index == 1) return _voltage; if (index == 2) return _active; if (index == 3) return _reactive; if (index == 4) return _apparent; if (index == 5) return _apparent > 0 ? 100 * _active / _apparent : 100; if (index == 6) return _energy[0].asDouble(); return 0; } protected: // --------------------------------------------------------------------- // Protected // --------------------------------------------------------------------- void _read() { static unsigned char state = 0; static unsigned long last = 0; static unsigned long ts = 0; static bool found = false; static unsigned char index = 0; if (state == 0) { while (_serial->available()) { _serial->flush(); found = true; ts = millis(); } if (found && (millis() - ts > V9261F_SYNC_INTERVAL)) { _serial->flush(); index = 0; state = 1; } } else if (state == 1) { while (_serial->available()) { _serial->read(); if (index++ >= 7) { _serial->flush(); index = 0; state = 2; } } } else if (state == 2) { while (_serial->available()) { _data[index] = _serial->read(); if (index++ >= 19) { _serial->flush(); ts = millis(); state = 3; } } } else if (state == 3) { if (_checksum()) { _active = (double) ( (_data[3]) + (_data[4] << 8) + (_data[5] << 16) + (_data[6] << 24) ) / _ratioP; _reactive = (double) ( (_data[7]) + (_data[8] << 8) + (_data[9] << 16) + (_data[10] << 24) ) / _ratioR; _voltage = (double) ( (_data[11]) + (_data[12] << 8) + (_data[13] << 16) + (_data[14] << 24) ) / _ratioV; _current = (double) ( (_data[15]) + (_data[16] << 8) + (_data[17] << 16) + (_data[18] << 24) ) / _ratioC; if (_active < 0) _active = 0; if (_reactive < 0) _reactive = 0; if (_voltage < 0) _voltage = 0; if (_current < 0) _current = 0; _apparent = fs_sqrt(_reactive * _reactive + _active * _active); if (last > 0) { _energy[0] += sensor::Ws { static_cast(_active * (millis() / last) / 1000) }; } last = millis(); } ts = millis(); index = 0; state = 4; } else if (state == 4) { while (_serial->available()) { _serial->flush(); ts = millis(); } if (millis() - ts > V9261F_SYNC_INTERVAL) { state = 1; } } } bool _checksum() { unsigned char checksum = 0; for (unsigned char i = 0; i < 19; i++) { checksum = checksum + _data[i]; } checksum = ~checksum + 0x33; return checksum == _data[19]; } // --------------------------------------------------------------------- unsigned int _pin_rx = V9261F_PIN; bool _inverted = V9261F_PIN_INVERSE; SoftwareSerial * _serial = NULL; double _active = 0; double _reactive = 0; double _voltage = 0; double _current = 0; double _apparent = 0; double _ratioP = V9261F_POWER_FACTOR; double _ratioC = V9261F_CURRENT_FACTOR; double _ratioV = V9261F_VOLTAGE_FACTOR; double _ratioR = V9261F_RPOWER_FACTOR; unsigned char _data[24]; }; #endif // SENSOR_SUPPORT && V9261F_SUPPORT