|
@ -0,0 +1,242 @@ |
|
|
|
|
|
// ----------------------------------------------------------------------------- |
|
|
|
|
|
// V9261F based power monitor |
|
|
|
|
|
// Copyright (C) 2017 by Xose Pérez <xose dot perez at gmail dot com> |
|
|
|
|
|
// ----------------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
|
#pragma once |
|
|
|
|
|
|
|
|
|
|
|
#include "Arduino.h" |
|
|
|
|
|
#include "BaseSensor.h" |
|
|
|
|
|
|
|
|
|
|
|
#include <SoftwareSerial.h> |
|
|
|
|
|
|
|
|
|
|
|
class V9261FSensor : public BaseSensor { |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------- |
|
|
|
|
|
// Public |
|
|
|
|
|
// --------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
|
V9261FSensor(): BaseSensor() { |
|
|
|
|
|
_count = 6; |
|
|
|
|
|
_sensor_id = SENSOR_V9261F_ID; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
_dirty = false; |
|
|
|
|
|
|
|
|
|
|
|
if (_serial) delete _serial; |
|
|
|
|
|
|
|
|
|
|
|
_serial = new SoftwareSerial(_pin_rx, SW_SERIAL_UNUSED_PIN, _inverted, 256); |
|
|
|
|
|
_serial->begin(V9261F_BAUDRATE); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Descriptive name of the sensor |
|
|
|
|
|
String description() { |
|
|
|
|
|
char buffer[28]; |
|
|
|
|
|
snprintf(buffer, sizeof(buffer), "V9261F @ SwSerial(%i,NULL)", _pin_rx); |
|
|
|
|
|
return String(buffer); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Loop-like method, call it in your main loop |
|
|
|
|
|
void tick() { |
|
|
|
|
|
_read(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Type for slot # index |
|
|
|
|
|
magnitude_t type(unsigned char index) { |
|
|
|
|
|
_error = SENSOR_ERROR_OK; |
|
|
|
|
|
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; |
|
|
|
|
|
_error = SENSOR_ERROR_OUT_OF_RANGE; |
|
|
|
|
|
return MAGNITUDE_NONE; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Current value for slot # index |
|
|
|
|
|
double value(unsigned char index) { |
|
|
|
|
|
_error = SENSOR_ERROR_OK; |
|
|
|
|
|
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; |
|
|
|
|
|
_error = SENSOR_ERROR_OUT_OF_RANGE; |
|
|
|
|
|
return 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected: |
|
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------- |
|
|
|
|
|
// Protected |
|
|
|
|
|
// --------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
|
void _read() { |
|
|
|
|
|
|
|
|
|
|
|
static unsigned char state = 0; |
|
|
|
|
|
static unsigned long last = 0; |
|
|
|
|
|
static bool found = false; |
|
|
|
|
|
static unsigned char index = 0; |
|
|
|
|
|
|
|
|
|
|
|
if (state == 0) { |
|
|
|
|
|
|
|
|
|
|
|
while (_serial->available()) { |
|
|
|
|
|
_serial->flush(); |
|
|
|
|
|
found = true; |
|
|
|
|
|
last = millis(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (found && (millis() - last > 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(); |
|
|
|
|
|
last = 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 = sqrt(_reactive * _reactive + _active * _active); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
last = millis(); |
|
|
|
|
|
index = 0; |
|
|
|
|
|
state = 4; |
|
|
|
|
|
|
|
|
|
|
|
} else if (state == 4) { |
|
|
|
|
|
|
|
|
|
|
|
while (_serial->available()) { |
|
|
|
|
|
_serial->flush(); |
|
|
|
|
|
last = millis(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (millis() - last > 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]; |
|
|
|
|
|
|
|
|
|
|
|
}; |