Browse Source

Initial support for V9261F-based multimeters

fastled
Xose Pérez 6 years ago
parent
commit
5f87e3ced3
6 changed files with 417 additions and 1 deletions
  1. +27
    -1
      code/espurna/config/hardware.h
  2. +19
    -0
      code/espurna/config/sensors.h
  3. +6
    -0
      code/espurna/espurna.ino
  4. +4
    -0
      code/espurna/hardware.ino
  5. +315
    -0
      code/espurna/v9261f.ino
  6. +46
    -0
      code/platformio.ini

+ 27
- 1
code/espurna/config/hardware.h View File

@ -71,9 +71,20 @@
// Buttons
#define BUTTON1_PIN 4
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define BUTTON1_RELAY 1
// Normal pushbutton
//#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
// Touch button
#define BUTTON1_MODE BUTTON_PUSHBUTTON
#define BUTTON1_PRESS BUTTON_MODE_TOGGLE
#define BUTTON1_CLICK BUTTON_MODE_NONE
#define BUTTON1_DBLCLICK BUTTON_MODE_NONE
#define BUTTON1_LNGCLICK BUTTON_MODE_NONE
#define BUTTON1_LNGLNGCLICK BUTTON_MODE_NONE
// Relays
#define RELAY1_PIN 12
#define RELAY1_TYPE RELAY_TYPE_INVERSE
@ -833,6 +844,21 @@
#define LIGHT_CH3_INVERSE 0
#define LIGHT_CH4_INVERSE 0
// -----------------------------------------------------------------------------
// V9261F
// -----------------------------------------------------------------------------
#elif defined(GENERIC_V9261F)
// Info
#define MANUFACTURER "GENERIC"
#define DEVICE "V9261F"
// V9261F
#define V9261F_SUPPORT 1
#define V9261F_PIN 2
#define V9261F_PIN_INVERSE 1
// -----------------------------------------------------------------------------
// Unknown hardware
// -----------------------------------------------------------------------------


+ 19
- 0
code/espurna/config/sensors.h View File

@ -155,6 +155,25 @@
#define HLW8012_MIN_CURRENT 0.05
#define HLW8012_MAX_CURRENT 10
//--------------------------------------------------------------------------------
// V9261F power sensor (Intertek)
// Enable support by passing V9261F_SUPPORT=1 build flag
//--------------------------------------------------------------------------------
#ifndef V9261F_SUPPORT
#define V9261F_SUPPORT 0
#endif
#define V9261F_SYNC_INTERVAL 600
#define V9261F_REPORT_INTERVAL 60000
#define V9261F_BAUDRATE 4800
#define V9261F_CURRENT_FACTOR 81156358
#define V9261F_VOLTAGE_FACTOR 4178508
#define V9261F_POWER_FACTOR 157859
#define V9261F_RPOWER_FACTOR V9261F_CURRENT_FACTOR
//--------------------------------------------------------------------------------
// Internal power montior
// Enable support by passing ADC_VCC_ENABLED=1 build flag


+ 6
- 0
code/espurna/espurna.ino View File

@ -271,6 +271,9 @@ void setup() {
#if HLW8012_SUPPORT
hlw8012Setup();
#endif
#if V9261F_SUPPORT
v9261fSetup();
#endif
#if DS18B20_SUPPORT
dsSetup();
#endif
@ -329,6 +332,9 @@ void loop() {
#if HLW8012_SUPPORT
hlw8012Loop();
#endif
#if V9261F_SUPPORT
v9261fLoop();
#endif
#if DS18B20_SUPPORT
dsLoop();
#endif


+ 4
- 0
code/espurna/hardware.ino View File

@ -434,6 +434,10 @@ void hwUpwardsCompatibility() {
setSetting("chLogic", 4, 0);
setSetting("relays", 1);
#elif defined(GENERIC_V9261F)
setSetting("board", 37);
#else
#error "UNSUPPORTED HARDWARE!"


+ 315
- 0
code/espurna/v9261f.ino View File

@ -0,0 +1,315 @@
/*
V9261F MODULE
Support for V9261D-based power monitors
Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
*/
#if V9261F_SUPPORT
#include <SoftwareSerial.h>
#include <ArduinoJson.h>
SoftwareSerial * _v9261f_uart;
bool _v9261f_enabled = false;
bool _v9261f_ready = false;
bool _v9261f_newdata = false;
int _v9261f_power = 0;
int _v9261f_rpower = 0;
int _v9261f_voltage = 0;
double _v9261f_current = 0;
unsigned char _v9261f_data[24];
// -----------------------------------------------------------------------------
// PRIVATE
// -----------------------------------------------------------------------------
void v9261fRead() {
static unsigned char state = 0;
static unsigned long last = 0;
static bool found = false;
static unsigned char index = 0;
if (state == 0) {
while (_v9261f_uart->available()) {
_v9261f_uart->flush();
found = true;
last = millis();
}
if (found && (millis() - last > V9261F_SYNC_INTERVAL)) {
_v9261f_uart->flush();
index = 0;
state = 1;
}
} else if (state == 1) {
while (_v9261f_uart->available()) {
_v9261f_uart->read();
if (index++ >= 7) {
_v9261f_uart->flush();
index = 0;
state = 2;
}
}
} else if (state == 2) {
while (_v9261f_uart->available()) {
_v9261f_data[index] = _v9261f_uart->read();
if (index++ >= 19) {
_v9261f_uart->flush();
last = millis();
state = 3;
}
}
} else if (state == 3) {
/*
for (unsigned char i=0; i<index;i++) {
DEBUG_MSG("%02X ", _v9261f_data[i]);
}
DEBUG_MSG("\n");
*/
if (checksumOK()) {
_v9261f_power = (double) (
(_v9261f_data[3]) +
(_v9261f_data[4] << 8) +
(_v9261f_data[5] << 16) +
(_v9261f_data[6] << 24)
) / V9261F_POWER_FACTOR;
_v9261f_rpower = (double) (
(_v9261f_data[7]) +
(_v9261f_data[8] << 8) +
(_v9261f_data[9] << 16) +
(_v9261f_data[10] << 24)
) / V9261F_RPOWER_FACTOR;
_v9261f_voltage = (double) (
(_v9261f_data[11]) +
(_v9261f_data[12] << 8) +
(_v9261f_data[13] << 16) +
(_v9261f_data[14] << 24)
) / V9261F_VOLTAGE_FACTOR;
_v9261f_current = (double) (
(_v9261f_data[15]) +
(_v9261f_data[16] << 8) +
(_v9261f_data[17] << 16) +
(_v9261f_data[18] << 24)
) / V9261F_CURRENT_FACTOR;
_v9261f_newdata = true;
/*
DEBUG_MSG_P(PSTR("[V9261F] W = %lu\n"), _v9261f_power);
DEBUG_MSG_P(PSTR("[V9261F] R = %lu\n"), _v9261f_rpower);
DEBUG_MSG_P(PSTR("[V9261F] V = %lu\n"), _v9261f_voltage);
DEBUG_MSG_P(PSTR("[V9261F] C = %lu\n"), _v9261f_current);
*/
}
last = millis();
index = 0;
state = 4;
} else if (state == 4) {
while (_v9261f_uart->available()) {
_v9261f_uart->flush();
last = millis();
}
if (millis() - last > V9261F_SYNC_INTERVAL) {
state = 1;
}
}
}
boolean checksumOK() {
unsigned char checksum = 0;
for (unsigned char i = 0; i < 19; i++) {
checksum = checksum + _v9261f_data[i];
}
checksum = ~checksum + 0x33;
return checksum == _v9261f_data[19];
}
// -----------------------------------------------------------------------------
// HAL
// -----------------------------------------------------------------------------
unsigned int getActivePower() {
return _v9261f_power;
}
unsigned int getReactivePower() {
return _v9261f_rpower;
}
unsigned int getApparentPower() {
return sqrt(_v9261f_rpower * _v9261f_rpower + _v9261f_power * _v9261f_power);
}
unsigned int getVoltage() {
return _v9261f_voltage;
}
double getCurrent() {
return _v9261f_current;
}
double getPowerFactor() {
unsigned int apparent = getApparentPower();
if (apparent > 0) return getActivePower() / getApparentPower();
return 1;
}
// -----------------------------------------------------------------------------
void v9261fSetup() {
_v9261f_uart = new SoftwareSerial(V9261F_PIN, SW_SERIAL_UNUSED_PIN, V9261F_PIN_INVERSE, 256);
_v9261f_uart->begin(V9261F_BAUDRATE);
// API definitions
#if WEB_SUPPORT
apiRegister(HLW8012_POWER_TOPIC, HLW8012_POWER_TOPIC, [](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("%d"), _v9261f_power);
});
apiRegister(HLW8012_CURRENT_TOPIC, HLW8012_CURRENT_TOPIC, [](char * buffer, size_t len) {
dtostrf(_v9261f_current, len-1, 3, buffer);
});
apiRegister(HLW8012_VOLTAGE_TOPIC, HLW8012_VOLTAGE_TOPIC, [](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("%d"), _v9261f_voltage);
});
#endif // WEB_SUPPORT
}
void v9261fLoop() {
static int sum_power = 0;
static int sum_rpower = 0;
static int sum_voltage = 0;
static double sum_current = 0;
static int count = 0;
// Sniff data in the UART interface
v9261fRead();
// Do we have new data?
if (_v9261f_newdata) {
_v9261f_newdata = false;
sum_power += getActivePower();
sum_rpower += getReactivePower();
sum_voltage += getVoltage();
sum_current += getCurrent();
count++;
#if WEB_SUPPORT
{
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
char buf_current[10];
dtostrf(getCurrent(), 6, 3, buf_current);
root["powVisible"] = 1;
root["powActivePower"] = getActivePower();
root["powCurrent"] = String(ltrim(buf_current));
root["powVoltage"] = getVoltage();
root["powApparentPower"] = getApparentPower();
root["powReactivePower"] = getReactivePower();
root["powPowerFactor"] = 100 * getPowerFactor();
String output;
root.printTo(output);
wsSend(output.c_str());
}
#endif
}
// Do we have to report?
static unsigned long last = 0;
if ((count == 0) || (millis() - last < V9261F_REPORT_INTERVAL)) return;
last = millis();
{
unsigned int power = sum_power / count;
unsigned int reactive = sum_rpower / count;
unsigned int voltage = sum_voltage / count;
double current = sum_current / count;
char buf_current[10];
dtostrf(current, 6, 3, buf_current);
unsigned int apparent = sqrt(power * power + reactive * reactive);
double energy_delta = (double) power * V9261F_REPORT_INTERVAL / 1000.0 / 3600.0;
char buf_energy[10];
dtostrf(energy_delta, 6, 3, buf_energy);
unsigned int factor = 100 * ((double) power / apparent);
// Report values to MQTT broker
mqttSend(HLW8012_POWER_TOPIC, String(power).c_str());
mqttSend(HLW8012_CURRENT_TOPIC, buf_current);
mqttSend(HLW8012_VOLTAGE_TOPIC, String(voltage).c_str());
mqttSend(HLW8012_ENERGY_TOPIC, buf_energy);
mqttSend(HLW8012_APOWER_TOPIC, String(apparent).c_str());
mqttSend(HLW8012_RPOWER_TOPIC, String(reactive).c_str());
mqttSend(HLW8012_PFACTOR_TOPIC, String(factor).c_str());
// Report values to Domoticz
#if DOMOTICZ_SUPPORT
{
char buffer[20];
snprintf_P(buffer, sizeof(buffer), PSTR("%d;%s"), power, buf_energy);
domoticzSend("dczPowIdx", 0, buffer);
snprintf_P(buffer, sizeof(buffer), PSTR("%s"), buf_energy);
domoticzSend("dczEnergyIdx", 0, buffer);
snprintf_P(buffer, sizeof(buffer), PSTR("%d"), voltage);
domoticzSend("dczVoltIdx", 0, buffer);
snprintf_P(buffer, sizeof(buffer), PSTR("%s"), buf_current);
domoticzSend("dczCurrentIdx", 0, buffer);
}
#endif
#if INFLUXDB_SUPPORT
{
influxDBSend(HLW8012_POWER_TOPIC, String(power).c_str());
influxDBSend(HLW8012_CURRENT_TOPIC, buf_current);
influxDBSend(HLW8012_VOLTAGE_TOPIC, String(voltage).c_str());
influxDBSend(HLW8012_ENERGY_TOPIC, buf_energy);
influxDBSend(HLW8012_APOWER_TOPIC, String(apparent).c_str());
influxDBSend(HLW8012_RPOWER_TOPIC, String(reactive).c_str());
influxDBSend(HLW8012_PFACTOR_TOPIC, String(factor).c_str());
}
#endif
// Reset counters
sum_power = sum_rpower = sum_voltage = sum_current = count = 0;
}
}
#endif

+ 46
- 0
code/platformio.ini View File

@ -22,6 +22,7 @@ lib_deps =
OneWire
DallasTemperature
Brzo I2C
EspSoftwareSerial
https://bitbucket.org/xoseperez/justwifi.git#1.1.4
https://bitbucket.org/xoseperez/hlw8012.git#1.0.1
https://bitbucket.org/xoseperez/fauxmoesp.git#2.2.0
@ -815,6 +816,51 @@ upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
monitor_baud = 115200
[env:wemos-v9261f]
platform = espressif8266
framework = arduino
board = d1_mini
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags} -DGENERIC_V9261F
upload_speed = 460800
monitor_baud = 115200
[env:wemos-v9261f-ota]
platform = espressif8266
framework = arduino
board = d1_mini
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags} -DGENERIC_V9261F
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
monitor_baud = 115200
[env:esp01-v9261f]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m} -DGENERIC_V9261F
monitor_baud = 115200
[env:esp01-v9261f-ota]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m} -DGENERIC_V9261F
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
monitor_baud = 115200
# ------------------------------------------------------------------------------
# GENERIC OTA ENVIRONMENTS
# ------------------------------------------------------------------------------


Loading…
Cancel
Save