Browse Source

Check CSE7766 implementation, added energy and calibration to web UI

rfm69
Xose Pérez 6 years ago
parent
commit
e7251d0191
5 changed files with 3471 additions and 5990 deletions
  1. BIN
      code/espurna/data/index.html.gz
  2. +73
    -0
      code/espurna/sensor.ino
  3. +140
    -41
      code/espurna/sensors/CSE7766Sensor.h
  4. +3251
    -5942
      code/espurna/static/index.html.gz.h
  5. +7
    -7
      code/html/index.html

BIN
code/espurna/data/index.html.gz View File


+ 73
- 0
code/espurna/sensor.ino View File

@ -154,6 +154,13 @@ void _sensorWebSocketStart(JsonObject& root) {
}
#endif
#if CSE7766_SUPPORT
if (sensor->getID() == SENSOR_CSE7766_ID) {
root["cseVisible"] = 1;
root["pwrVisible"] = 1;
}
#endif
#if V9261F_SUPPORT
if (sensor->getID() == SENSOR_V9261F_ID) {
root["pwrVisible"] = 1;
@ -313,6 +320,14 @@ void _sensorLoad() {
}
#endif
#if CSE7766_SUPPORT
{
CSE7766Sensor * sensor = new CSE7766Sensor();
sensor->setRX(CSE7766_PIN);
_sensors.push_back(sensor);
}
#endif
#if DALLAS_SUPPORT
{
DallasSensor * sensor = new DallasSensor();
@ -586,6 +601,27 @@ void _sensorInit() {
#endif // HLW8012_SUPPORT
#if CSE7766_SUPPORT
if (_sensors[i]->getID() == SENSOR_CSE7766_ID) {
CSE7766Sensor * sensor = (CSE7766Sensor *) _sensors[i];
double value;
value = getSetting("pwrRatioC", 0).toFloat();
if (value > 0) sensor->setCurrentRatio(value);
value = getSetting("pwrRatioV", 0).toFloat();
if (value > 0) sensor->setVoltageRatio(value);
value = getSetting("pwrRatioP", 0).toFloat();
if (value > 0) sensor->setPowerRatio(value);
}
#endif // CSE7766_SUPPORT
}
}
@ -688,6 +724,43 @@ void _sensorConfigure() {
#endif // HLW8012_SUPPORT
#if CSE7766_SUPPORT
if (_sensors[i]->getID() == SENSOR_CSE7766_ID) {
double value;
CSE7766Sensor * sensor = (CSE7766Sensor *) _sensors[i];
if (value = getSetting("pwrExpectedC", 0).toFloat()) {
sensor->expectedCurrent(value);
setSetting("pwrRatioC", sensor->getCurrentRatio());
}
if (value = getSetting("pwrExpectedV", 0).toInt()) {
sensor->expectedVoltage(value);
setSetting("pwrRatioV", sensor->getVoltageRatio());
}
if (value = getSetting("pwrExpectedP", 0).toInt()) {
sensor->expectedPower(value);
setSetting("pwrRatioP", sensor->getPowerRatio());
}
if (getSetting("pwrResetE", 0).toInt() == 1) {
sensor->resetEnergy();
}
if (getSetting("pwrResetCalibration", 0).toInt() == 1) {
sensor->resetRatios();
delSetting("pwrRatioC");
delSetting("pwrRatioV");
delSetting("pwrRatioP");
}
}
#endif // CSE7766_SUPPORT
}
// Update filter sizes


+ 140
- 41
code/espurna/sensors/CSE7766Sensor.h View File

@ -54,6 +54,58 @@ class CSE7766Sensor : public BaseSensor {
return _inverted;
}
// ---------------------------------------------------------------------
void expectedCurrent(double expected) {
if ((expected > 0) && (_current > 0)) {
_ratioC = _ratioC * (expected / _current);
}
}
void expectedVoltage(unsigned int expected) {
if ((expected > 0) && (_voltage > 0)) {
_ratioV = _ratioV * (expected / _voltage);
}
}
void expectedPower(unsigned int expected) {
if ((expected > 0) && (_active > 0)) {
_ratioP = _ratioP * (expected / _active);
}
}
void setCurrentRatio(double value) {
_ratioC = value;
};
void setVoltageRatio(double value) {
_ratioV = value;
};
void setPowerRatio(double value) {
_ratioP = value;
};
double getCurrentRatio() {
return _ratioC;
};
double getVoltageRatio() {
return _ratioV;
};
double getPowerRatio() {
return _ratioP;
};
void resetRatios() {
_ratioC = _ratioV = _ratioP = 1.0;
}
void resetEnergy() {
_energy = 0;
}
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
@ -65,9 +117,13 @@ class CSE7766Sensor : public BaseSensor {
if (_serial) delete _serial;
_serial = new SoftwareSerial(_pin_rx, SW_SERIAL_UNUSED_PIN, _inverted, 32);
_serial->enableIntTx(false);
_serial->begin(CSE7766_BAUDRATE);
if (1 == _pin_rx) {
Serial.begin(CSE7766_BAUDRATE);
} else {
_serial = new SoftwareSerial(_pin_rx, SW_SERIAL_UNUSED_PIN, _inverted, 32);
_serial->enableIntTx(false);
_serial->begin(CSE7766_BAUDRATE);
}
_ready = true;
_dirty = false;
@ -77,7 +133,11 @@ class CSE7766Sensor : public BaseSensor {
// Descriptive name of the sensor
String description() {
char buffer[28];
snprintf(buffer, sizeof(buffer), "CSE7766 @ SwSerial(%u,NULL)", _pin_rx);
if (1 == _pin_rx) {
snprintf(buffer, sizeof(buffer), "CSE7766 @ HwSerial");
} else {
snprintf(buffer, sizeof(buffer), "CSE7766 @ SwSerial(%u,NULL)", _pin_rx);
}
return String(buffer);
}
@ -137,11 +197,21 @@ class CSE7766Sensor : public BaseSensor {
void _process() {
// Sample data:
// 55 5A 02 E9 50 00 03 31 00 3E 9E 00 0D 30 4F 44 F8 00 12 65 F1 81 76 72 (w/ load)
// F2 5A 02 E9 50 00 03 2B 00 3E 9E 02 D7 7C 4F 44 F8 CF A5 5D E1 B3 2A B4 (w/o load)
#if SENSOR_DEBUG
DEBUG_MSG("[SENSOR] CSE7766: _process: ");
for (byte i=0; i<24; i++) DEBUG_MSG("%02X ", _data[i]);
DEBUG_MSG("\n");
#endif
// Checksum
if (!_checksum()) {
_error = SENSOR_ERROR_CRC;
#if SENSOR_DEBUG
DEBUG_MSG_P(PSTR("[SENSOR] CSE7766: Checksum error"));
DEBUG_MSG("[SENSOR] CSE7766: Checksum error\n");
#endif
return;
}
@ -150,7 +220,7 @@ class CSE7766Sensor : public BaseSensor {
if (0xAA == _data[0]) {
_error = SENSOR_ERROR_CALIBRATION;
#if SENSOR_DEBUG
DEBUG_MSG_P(PSTR("[SENSOR] CSE7766: Chip not calibrated"));
DEBUG_MSG("[SENSOR] CSE7766: Chip not calibrated\n");
#endif
return;
}
@ -158,39 +228,35 @@ class CSE7766Sensor : public BaseSensor {
if ((_data[0] & 0xFC) > 0xF0) {
_error = SENSOR_ERROR_OTHER;
#if SENSOR_DEBUG
if (0xF1 == _data[0] & 0xF1) DEBUG_MSG_P(PSTR("[SENSOR] CSE7766: Abnormal coefficient storage area"));
if (0xF2 == _data[0] & 0xF2) DEBUG_MSG_P(PSTR("[SENSOR] CSE7766: Power cycle exceeded range"));
if (0xF4 == _data[0] & 0xF4) DEBUG_MSG_P(PSTR("[SENSOR] CSE7766: Current cycle exceeded range"));
if (0xF8 == _data[0] & 0xF8) DEBUG_MSG_P(PSTR("[SENSOR] CSE7766: Voltage cycle exceeded range"));
if (0xF1 == _data[0] & 0xF1) DEBUG_MSG("[SENSOR] CSE7766: Abnormal coefficient storage area\n");
if (0xF2 == _data[0] & 0xF2) DEBUG_MSG("[SENSOR] CSE7766: Power cycle exceeded range\n");
if (0xF4 == _data[0] & 0xF4) DEBUG_MSG("[SENSOR] CSE7766: Current cycle exceeded range\n");
if (0xF8 == _data[0] & 0xF8) DEBUG_MSG("[SENSOR] CSE7766: Voltage cycle exceeded range\n");
#endif
return;
}
// Calibration coefficients
if (0 == _coefV) {
_coefV = (_data[2] << 16 | _data[3] << 8 | _data[4]) / 100;
_coefV *= 100;
_coefC = (_data[8] << 16 | _data[9] << 8 | _data[10]);
_coefP = (_data[14] << 16 | _data[15] << 8 | _data[16]) / 1000;
_coefP *= 1000;
}
unsigned long _coefV = (_data[2] << 16 | _data[3] << 8 | _data[4] ); // 190770
unsigned long _coefC = (_data[8] << 16 | _data[9] << 8 | _data[10]); // 16030
unsigned long _coefP = (_data[14] << 16 | _data[15] << 8 | _data[16]); // 5195000
// Adj: this looks like a sampling report
uint8_t adj = _data[20];
uint8_t adj = _data[20]; // F1 11110001
// Calculate voltage
_voltage = 0;
if ((adj & 0x40) == 0x40) {
unsigned long voltage_cycle = _data[5] << 16 | _data[6] << 8 | _data[7];
_voltage = _coefV / voltage_cycle / CSE7766_V2R;
unsigned long voltage_cycle = _data[5] << 16 | _data[6] << 8 | _data[7]; // 817
_voltage = _ratioV * _coefV / voltage_cycle / CSE7766_V2R; // 190700 / 817 = 233.41
}
// Calculate power
_active = 0;
if ((adj & 0x10) == 0x10) {
if ((_data[0] & 0xF2) != 0xF2) {
unsigned long power_cycle = _data[17] << 16 | _data[18] << 8 | _data[19];
_active = _coefP / power_cycle / CSE7766_V1R / CSE7766_V2R;
unsigned long power_cycle = _data[17] << 16 | _data[18] << 8 | _data[19]; // 4709
_active = _ratioP * _coefP / power_cycle / CSE7766_V1R / CSE7766_V2R; // 5195000 / 4709 = 1103.20
}
}
@ -198,19 +264,17 @@ class CSE7766Sensor : public BaseSensor {
_current = 0;
if ((adj & 0x20) == 0x20) {
if (_active > 0) {
unsigned long current_cycle = _data[11] << 16 | _data[12] << 8 | _data[13];
_current = _coefC / current_cycle / CSE7766_V1R;
unsigned long current_cycle = _data[11] << 16 | _data[12] << 8 | _data[13]; // 3376
_current = _ratioC * _coefC / current_cycle / CSE7766_V1R; // 16030 / 3376 = 4.75
}
}
// Calculate energy
/*
static unsigned long cf_pulses_last = 0;
unsigned long cf_pulses = _data[21] << 8 | _data[22];
unsigned long frequency = cf_pulses - cf_pulses_last;
if (0 == cf_pulses_last) cf_pulses_last = cf_pulses;
_energy += (cf_pulses - cf_pulses_last) * (float) _coefP / 1000000.0;
cf_pulses_last = cf_pulses;
_energy += (100000 * frequency * _coefP);
*/
}
@ -221,26 +285,35 @@ class CSE7766Sensor : public BaseSensor {
static unsigned char index = 0;
static unsigned long last = millis();
while (_serial->available()) {
while (_serial_available()) {
// A 24 bytes message takes ~55ms to go through at 4800 bps
// Reset counter if more than 1000ms have passed since last byte.
if (millis() - last > CSE7766_SYNC_INTERVAL) index = 0;
last = millis();
uint8_t byte = _serial->read();
uint8_t byte = _serial_read();
// first byte must be 0x55 or 0xF?
if (0 == index) {
if ((0x55 != byte) && (byte < 0xF0)) {
continue;
}
// second byte in packet must be 0x5A
if ((1 == index) && (0xA5 != byte)) {
index = 0;
} else {
_data[index++] = byte;
if (index > 23) {
_serial->flush();
break;
// second byte must be 0x5A
} else if (1 == index) {
if (0x5A != byte) {
index = 0;
continue;
}
}
_data[index++] = byte;
if (index > 23) {
_serial_flush();
break;
}
}
// Process packet
@ -253,6 +326,32 @@ class CSE7766Sensor : public BaseSensor {
// ---------------------------------------------------------------------
bool _serial_available() {
if (1 == _pin_rx) {
return Serial.available();
} else {
return _serial->available();
}
}
void _serial_flush() {
if (1 == _pin_rx) {
return Serial.flush();
} else {
return _serial->flush();
}
}
uint8_t _serial_read() {
if (1 == _pin_rx) {
return Serial.read();
} else {
return _serial->read();
}
}
// ---------------------------------------------------------------------
unsigned int _pin_rx = CSE7766_PIN;
bool _inverted = CSE7766_PIN_INVERSE;
SoftwareSerial * _serial = NULL;
@ -262,9 +361,9 @@ class CSE7766Sensor : public BaseSensor {
double _current = 0;
double _energy = 0;
unsigned long _coefV = 0;
unsigned long _coefC = 0;
unsigned long _coefP = 0;
double _ratioV = 1.0;
double _ratioC = 1.0;
double _ratioP = 1.0;
unsigned char _data[24];


+ 3251
- 5942
code/espurna/static/index.html.gz.h
File diff suppressed because it is too large
View File


+ 7
- 7
code/html/index.html View File

@ -1061,7 +1061,7 @@
</select>
</div>
<div class="pure-g module module-hlw module-emon">
<div class="pure-g module module-hlw module-cse module-emon">
<label class="pure-u-1 pure-u-lg-1-4">Energy units</label>
<select name="energyUnits" tabindex="16" class="pure-u-1 pure-u-lg-1-4">
<option value="0">Joules (J)</option>
@ -1097,7 +1097,7 @@
</div>
</div>
<legend class="module module-hlw module-emon">Energy monitor</legend>
<legend class="module module-hlw module-cse module-emon">Energy monitor</legend>
<div class="pure-g module module-emon">
<label class="pure-u-1 pure-u-lg-1-4">Voltage</label>
@ -1106,28 +1106,28 @@
<div class="pure-u-1 pure-u-lg-3-4 hint">Mains voltage in your system (in V).</div>
</div>
<div class="pure-g module module-hlw">
<div class="pure-g module module-hlw module-cse">
<label class="pure-u-1 pure-u-lg-1-4">Expected Current</label>
<input class="pure-u-1 pure-u-lg-3-4 pwrExpected" name="pwrExpectedC" type="text" tabindex="52" placeholder="0" />
<div class="pure-u-0 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint">In Amperes (A). If you are using a pure resistive load like a bulb, this will be the ratio between the two previous values, i.e. power / voltage. You can also use a current clamp around one of the power wires to get this value.</div>
</div>
<div class="pure-g module module-hlw">
<div class="pure-g module module-hlw module-cse">
<label class="pure-u-1 pure-u-lg-1-4">Expected Voltage</label>
<input class="pure-u-1 pure-u-lg-3-4 pwrExpected" name="pwrExpectedV" type="text" tabindex="53" placeholder="0" />
<div class="pure-u-0 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint">In Volts (V). Enter your the nominal AC voltage for your household or facility, or use multimeter to get this value.</div>
</div>
<div class="pure-g module module-hlw module-emon">
<div class="pure-g module module-hlw module-cse module-emon">
<label class="pure-u-1 pure-u-lg-1-4">Expected Power</label>
<input class="pure-u-1 pure-u-lg-3-4 pwrExpected" name="pwrExpectedP" type="text" tabindex="54" placeholder="0" />
<div class="pure-u-0 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint">In Watts (W). Calibrate your sensor connecting a pure resistive load (like a bulb) and enter here the its nominal power or use a multimeter.</div>
</div>
<div class="pure-g module module-hlw module-emon">
<div class="pure-g module module-hlw module-cse module-emon">
<label class="pure-u-1 pure-u-lg-1-4">Reset calibration</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="pwrResetCalibration" tabindex="55" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
@ -1135,7 +1135,7 @@
<div class="pure-u-1 pure-u-lg-3-4 hint">Move this switch to ON and press "Save" to revert to factory calibration values.</div>
</div>
<div class="pure-g module module-hlw module-emon">
<div class="pure-g module module-hlw module-cse module-emon">
<label class="pure-u-1 pure-u-lg-1-4">Reset energy</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="pwrResetE" tabindex="56" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>


Loading…
Cancel
Save