Browse Source

Preliminary support for Sonoff POW

fastled
Xose Pérez 8 years ago
parent
commit
7f9a7ef052
16 changed files with 406 additions and 45 deletions
  1. +3
    -0
      .gitmodules
  2. +5
    -3
      README.md
  3. +14
    -14
      code/.travis.yml
  4. +4
    -0
      code/html/custom.css
  5. +9
    -1
      code/html/custom.js
  6. +10
    -21
      code/html/index.html
  7. +1
    -0
      code/lib/HLW8012
  8. +10
    -2
      code/platformio.custom.ini
  9. +9
    -1
      code/platformio.ini
  10. +136
    -0
      code/platformio.official.ini
  11. +19
    -0
      code/src/defaults.h
  12. +2
    -2
      code/src/dht.ino
  13. +6
    -0
      code/src/main.ino
  14. +160
    -0
      code/src/pow.ino
  15. +1
    -1
      code/src/version.h
  16. +17
    -0
      code/src/websockets.ino

+ 3
- 0
.gitmodules View File

@ -13,3 +13,6 @@
[submodule "code/lib/JustWifi"]
path = code/lib/JustWifi
url = https://bitbucket.org/xoseperez/justwifi
[submodule "code/lib/HLW8012"]
path = code/lib/HLW8012
url = https://bitbucket.org/xoseperez/hlw8012.git

+ 5
- 3
README.md View File

@ -13,21 +13,22 @@ You can read about this board and firmware in [my blog][2].
* [ITead Sonoff RF][8]
* [ITead Slampher][9]
* [ITead S20 Smart Socket][10]
* [ITead Sonoff POW][15]
* Tinkerman ESPurna board
## Features
* **WebServer for configuration** and simple relay toggle
* **Flashing firmware Over-The-Air** (OTA)
* Up to **3 configurable WIFI networks**
* Up to **3 configurable WIFI networks**, connects to the strongest signal
* **MQTT support** with configurable host and topic
* Manual switch ON/OFF with button (single click the button)
* AP mode backup (double click the button)
* Visual status of the connection via the LED
* Support for custom **[RF module][2]**
* Support for **automatic over-the-air updates** through the [NoFUSS Library][6]
* Support for **current monitoring** through then [EmonLiteESP Library][7]
* Support for **DHT22** sensors
* Support for the **HLW8012** power sensors present in the Sonoff POW
* Support for **current monitoring** through then [EmonLiteESP Library][7] using a non-intrusive current sensor (requires some hacking)
* Command line configuration
## Installing
@ -157,3 +158,4 @@ After flashing the firmware via serial do a hard reset of the device (unplug & p
[12]: https://docs.npmjs.com/getting-started/installing-node
[13]: https://nodejs.org/en/
[14]: https://www.npmjs.com/
[15]: https://www.itead.cc/sonoff-pow.html

+ 14
- 14
code/.travis.yml View File

@ -24,20 +24,20 @@
# Template #1: General project. Test it using existing `platformio.ini`.
#
# language: python
# python:
# - "2.7"
#
# sudo: false
# cache:
# directories:
# - "~/.platformio"
#
# install:
# - pip install -U platformio
#
# script:
# - platformio run
language: python
python:
- "2.7"
sudo: false
cache:
directories:
- "~/.platformio"
install:
- pip install -U platformio
script:
- platformio run
#


+ 4
- 0
code/html/custom.css View File

@ -54,3 +54,7 @@ div.hint {
color: #ccc;
margin-top: -5px;
}
.module {
display: none;
}

+ 9
- 1
code/html/custom.js View File

@ -3,6 +3,7 @@ var websock;
function doUpdate() {
var data = $("#formSave").serializeArray();
websock.send(JSON.stringify({'config': data}));
$(".powExpected").val(0);
return false;
}
@ -72,6 +73,14 @@ function processData(data) {
// automatic assign
Object.keys(data).forEach(function(key) {
// Enable options
if (key.endsWith("Visible")) {
var module = key.slice(0,-7);
console.log(module);
$(".module-" + module).show();
return;
}
// Look for INPUTs
var element = $("input[name=" + key + "]");
if (element.length > 0) {
@ -127,7 +136,6 @@ function init() {
$(".pure-menu-link").on('click', showPanel);
var host = window.location.hostname;
//host = "studiolamp.local";
websock = new WebSocket('ws://' + host + ':81/');
websock.onopen = function(evt) {};
websock.onclose = function(evt) {};


+ 10
- 21
code/html/index.html View File

@ -48,7 +48,7 @@
<a href="#" class="pure-menu-link" data="panel-mqtt">MQTT</a>
</li>
<li class="pure-menu-item">
<li class="pure-menu-item module module-pow">
<a href="#" class="pure-menu-link" data="panel-power">POWER</a>
</li>
@ -112,19 +112,19 @@
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="mqttStatus" readonly />
</div>
<div class="pure-g">
<div class="pure-g module module-dht">
<label class="pure-u-1 pure-u-sm-1-4" for="dhtTmp">Temperature (ºC)</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="dhtTmp" readonly />
</div>
<div class="pure-g">
<div class="pure-g module module-dht">
<label class="pure-u-1 pure-u-sm-1-4" for="dhtHum">Humidity (%)</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="dhtHum" readonly />
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="emonPower">Power (W)</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="emonPower" readonly />
<div class="pure-g module module-pow">
<label class="pure-u-1 pure-u-sm-1-4" for="powActivePower">Power (W)</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="powActivePower" readonly />
</div>
<div class="pure-g">
@ -276,7 +276,7 @@
<div class="header">
<h1>POWER</h1>
<h2>
Configure your power monitor variables.
Calibrate your power monitor device. Use a pure resistive load and introduce the expected values for active power, current and voltage. Use the nominal values or a multimeter to get the proper numbers. Ste any field to 0 to leave the calibration value untouched.
</h2>
</div>
@ -287,20 +287,9 @@
<div class="pure-g">
<div class="pure-u-1">
<label class="form-label" for="emonMains">AC RMS Voltage</label>
<div class="hint">This is your house nominal voltage, you probably know this or you wont be playing with this device...</div>
<select name="emonMains" class="pure-u-1-4" tabindex="40">
<option value="125">125</a>
<option value="220">220</a>
<option value="230">230</a>
<option value="240">240</a>
</select>
</div>
<div class="pure-u-1">
<label class="form-label" for="emonRatio">Current Ratio</label>
<div class="hint">This is the value in amps for a 1V output for your sensor. Some current sensors like the YHDC SCT-013-030 have it written in the enclosure: 30A 1V. If you are using a current sensor that outputs a current (no built in burden resistor) it will depend on the turns ratio between the primary and secondary coils in the sensor and the burden resistor you use. Check about this constant in the <a href="https://openenergymonitor.org/emon/buildingblocks/calibration" target="_blank">post about calibration</a> in the Open Energy Monitor site.</div>
<input name="emonRatio" type="text" class="pure-u-1-4" value="" size="8" tabindex="41" placeholder="0">
<label class="form-label" for="powExpectedPower">AC RMS Active Power</label>
<div class="hint">If you are using a pure resistive load like a bulb this will be writen on it, otherwise use a socket multimeter to get this value.</div>
<input name="powExpectedPower" type="text" class="pure-u-1-4 powExpected" size="8" tabindex="40" placeholder="0">
</div>
</div>


+ 1
- 0
code/lib/HLW8012

@ -0,0 +1 @@
Subproject commit 1dfa68b9e79a4a8ea96df4ed20f64e0c12ee9d26

+ 10
- 2
code/platformio.custom.ini View File

@ -12,7 +12,7 @@ env_default = node-debug
[common]
platform = espressif8266
framework = arduino
lib_install = 19,44,64,89,549,727
lib_install = 19,31,44,64,89,549,727
extra_script = pio_hooks.py
[ota]
@ -28,6 +28,14 @@ build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF
[env:sonoff-debug-ota]
include = env:sonoff-debug,ota
[env:sonoff-pow-debug]
include = common
board = esp01_1m
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF_POW
[env:sonoff-pow-debug-ota]
include = env:sonoff-pow-debug,ota
[env:slampher-debug]
include = common
board = esp01_1m
@ -71,4 +79,4 @@ include = env:sonoff-debug-ota
[env:living-lamp-device]
topic = /home/living/lamp/ip
include = env:sonoff-debug-ota
include = env:s20-debug-ota

+ 9
- 1
code/platformio.ini View File

@ -1,5 +1,5 @@
[platformio]
env_default = node-debug
env_default = sonoff-pow-debug
[env:sonoff-debug]
platform = espressif8266
@ -20,6 +20,14 @@ upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
[env:sonoff-pow-debug]
platform = espressif8266
framework = arduino
lib_install = 19,31,44,64,89,549,727
extra_script = pio_hooks.py
board = esp01_1m
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF_POW
[env:slampher-debug]
platform = espressif8266
framework = arduino


+ 136
- 0
code/platformio.official.ini View File

@ -0,0 +1,136 @@
[platformio]
env_default = sonoff-pow-debug
[env:sonoff-debug]
platform = espressif8266
framework = arduino
lib_install = 19,31,44,64,89,549,727
extra_script = pio_hooks.py
board = esp01_1m
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF
[env:sonoff-debug-ota]
platform = espressif8266
framework = arduino
lib_install = 19,31,44,64,89,549,727
extra_script = pio_hooks.py
board = esp01_1m
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
[env:sonoff-pow-debug]
platform = espressif8266
framework = arduino
lib_install = 19,31,44,64,89,549,727
extra_script = pio_hooks.py
board = esp01_1m
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF_POW
[env:slampher-debug]
platform = espressif8266
framework = arduino
lib_install = 19,31,44,64,89,549,727
extra_script = pio_hooks.py
board = esp01_1m
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSLAMPHER
[env:slampher-debug-ota]
platform = espressif8266
framework = arduino
lib_install = 19,31,44,64,89,549,727
extra_script = pio_hooks.py
board = esp01_1m
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSLAMPHER
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
[env:s20-debug]
platform = espressif8266
framework = arduino
lib_install = 19,31,44,64,89,549,727
extra_script = pio_hooks.py
board = esp01_1m
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DS20
[env:s20-debug-ota]
platform = espressif8266
framework = arduino
lib_install = 19,31,44,64,89,549,727
extra_script = pio_hooks.py
board = esp01_1m
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DS20
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
[env:node-debug]
platform = espressif8266
framework = arduino
lib_install = 19,31,44,64,89,549,727
extra_script = pio_hooks.py
board = nodemcuv2
build_flags = -DNODEMCUV2 -DDEBUG_PORT=Serial
[env:node-debug-ota]
platform = espressif8266
framework = arduino
lib_install = 19,31,44,64,89,549,727
extra_script = pio_hooks.py
board = nodemcuv2
build_flags = -DNODEMCUV2 -DDEBUG_PORT=Serial
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
[env:ac-device]
topic = /home/cellar/airconditioner/ip
platform = espressif8266
framework = arduino
lib_install = 19,31,44,64,89,549,727
extra_script = pio_hooks.py
board = esp01_1m
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DS20
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
[env:washer-device]
topic = /home/cellar/washer/ip
platform = espressif8266
framework = arduino
lib_install = 19,31,44,64,89,549,727
extra_script = pio_hooks.py
board = esp01_1m
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF -DENABLE_EMON=1 -DENABLE_DHT=1
[env:studio-lamp-device]
topic = /home/studio/lamp/ip
platform = espressif8266
framework = arduino
lib_install = 19,31,44,64,89,549,727
extra_script = pio_hooks.py
board = esp01_1m
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
[env:living-lamp-device]
topic = /home/living/lamp/ip
platform = espressif8266
framework = arduino
lib_install = 19,31,44,64,89,549,727
extra_script = pio_hooks.py
board = esp01_1m
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266

+ 19
- 0
code/src/defaults.h View File

@ -14,6 +14,7 @@
//#define ENABLE_EMON 1
//#define ENABLE_DHT 1
//#define ENABLE_RF 1
//#define ENABLE_POW 1
// -----------------------------------------------------------------------------
// HARDWARE
@ -35,6 +36,13 @@
#define LED_PIN 13
#endif
#ifdef SONOFF_POW
#define ENABLE_POW 1
#define MANUFACTURER "ITEAD"
#define DEVICE "SONOFF_POW"
#define LED_PIN 13
#endif
#ifdef SLAMPHER
#define MANUFACTURER "ITEAD"
#define DEVICE "SLAMPHER"
@ -124,3 +132,14 @@
#define EMON_MAINS_VOLTAGE 230
#define EMON_CURRENT_RATIO 180
#define EMON_POWER_TOPIC "/power"
#define POW_SEL_PIN 5
#define POW_CF1_PIN 13
#define POW_CF_PIN 14
#define POW_SEL_CURRENT HIGH
#define POW_CURRENT_R 0.001
#define POW_VOLTAGE_R_UP ( 5 * 470000 ) // Real: 2280k
#define POW_VOLTAGE_R_DOWN ( 1000 ) // Real 1.009k
#define POW_POWER_TOPIC "/power"
#define POW_UPDATE_INTERVAL 10000
#define POW_REPORT_EVERY 6

+ 2
- 2
code/src/dht.ino View File

@ -64,8 +64,8 @@ void dhtLoop() {
mqttSend((char *) getSetting("dhtHumTopic", DHT_HUMIDITY_TOPIC).c_str(), humidity);
// Update websocket clients
char buffer[20];
sprintf_P(buffer, PSTR("{\"dhtTmp\": %s, \"dhtHum\": %s}"), temperature, humidity);
char buffer[100];
sprintf_P(buffer, PSTR("{\"dhtVisible\": 1, \"dhtTmp\": %s, \"dhtHum\": %s}"), temperature, humidity);
webSocketSend(buffer);
}


+ 6
- 0
code/src/main.ino View File

@ -151,6 +151,9 @@ void setup() {
#if ENABLE_NOFUSS
nofussSetup();
#endif
#if ENABLE_POW
powSetup();
#endif
#if ENABLE_DHT
dhtSetup();
#endif
@ -178,6 +181,9 @@ void loop() {
#if ENABLE_NOFUSS
nofussLoop();
#endif
#if ENABLE_POW
powLoop();
#endif
#if ENABLE_DHT
dhtLoop();
#endif


+ 160
- 0
code/src/pow.ino View File

@ -0,0 +1,160 @@
/*
ESPurna
POW MODULE
Support for Sonoff POW HLW8012-based power monitor
Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com>
*/
#if ENABLE_POW
#include <HLW8012.h>
HLW8012 hlw8012;
// -----------------------------------------------------------------------------
// POW
// -----------------------------------------------------------------------------
// When using interrupts we have to call the library entry point
// whenever an interrupt is triggered
void hlw8012_cf1_interrupt() {
hlw8012.cf1_interrupt();
}
void hlw8012_cf_interrupt() {
hlw8012.cf_interrupt();
}
void powAttachInterrupts() {
//attachInterrupt(POW_CF1_PIN, hlw8012_cf1_interrupt, CHANGE);
attachInterrupt(POW_CF_PIN, hlw8012_cf_interrupt, CHANGE);
DEBUG_MSG("[POW] Enabled\n");
}
void powDettachInterrupts() {
//detachInterrupt(POW_CF1_PIN);
detachInterrupt(POW_CF_PIN);
DEBUG_MSG("[POW] Disabled\n");
}
void powSaveCalibration() {
setSetting("powPowerMult", String() + hlw8012.getPowerMultiplier());
setSetting("powCurrentMult", String() + hlw8012.getCurrentMultiplier());
setSetting("powVoltageMult", String() + hlw8012.getVoltageMultiplier());
}
void powRetrieveCalibration() {
double value;
value = getSetting("powPowerMult", "0").toFloat();
if (value > 0) hlw8012.setPowerMultiplier((int) value);
value = getSetting("powCurrentMult", "0").toFloat();
if (value > 0) hlw8012.setCurrentMultiplier((int) value);
value = getSetting("powVoltageMult", "0").toFloat();
if (value > 0) hlw8012.setVoltageMultiplier((int) value);
}
void powSetExpectedActivePower(unsigned int power) {
if (power > 0) {
hlw8012.expectedActivePower(power);
powSaveCalibration();
}
}
void powSetExpectedCurrent(double current) {
if (current > 0) {
hlw8012.expectedCurrent(current);
powSaveCalibration();
}
}
void powSetExpectedVoltage(unsigned int voltage) {
if (voltage > 0) {
hlw8012.expectedVoltage(voltage);
powSaveCalibration();
}
}
unsigned int getActivePower() {
return hlw8012.getActivePower();
}
unsigned int getApparentPower() {
return hlw8012.getApparentPower();
}
double getCurrent() {
return hlw8012.getCurrent();
}
unsigned int getVoltage() {
return hlw8012.getVoltage();
}
unsigned int getPowerFactor() {
return (int) (100 * hlw8012.getPowerFactor());
}
void powSetup() {
// Initialize HLW8012
// void begin(unsigned char cf_pin, unsigned char cf1_pin, unsigned char sel_pin, unsigned char currentWhen = HIGH, bool use_interrupts = false, unsigned long pulse_timeout = PULSE_TIMEOUT);
// * cf_pin, cf1_pin and sel_pin are GPIOs to the HLW8012 IC
// * currentWhen is the value in sel_pin to select current sampling
// * set use_interrupts to true to use interrupts to monitor pulse widths
// * leave pulse_timeout to the default value, recommended when using interrupts
hlw8012.begin(POW_CF_PIN, POW_CF1_PIN, POW_SEL_PIN, POW_SEL_CURRENT, true);
// These values are used to calculate current, voltage and power factors as per datasheet formula
// These are the nominal values for the Sonoff POW resistors:
// * The CURRENT_RESISTOR is the 1milliOhm copper-manganese resistor in series with the main line
// * The VOLTAGE_RESISTOR_UPSTREAM are the 5 470kOhm resistors in the voltage divider that feeds the V2P pin in the HLW8012
// * The VOLTAGE_RESISTOR_DOWNSTREAM is the 1kOhm resistor in the voltage divider that feeds the V2P pin in the HLW8012
hlw8012.setResistors(POW_CURRENT_R, POW_VOLTAGE_R_UP, POW_VOLTAGE_R_DOWN);
powRetrieveCalibration();
static WiFiEventHandler e1 = WiFi.onStationModeDisconnected([](const WiFiEventStationModeDisconnected& event) {
powDettachInterrupts();
});
static WiFiEventHandler e2 = WiFi.onSoftAPModeStationDisconnected([](const WiFiEventSoftAPModeStationDisconnected& event) {
powDettachInterrupts();
});
static WiFiEventHandler e3 = WiFi.onStationModeConnected([](const WiFiEventStationModeConnected& event) {
powAttachInterrupts();
});
static WiFiEventHandler e4 = WiFi.onSoftAPModeStationConnected([](const WiFiEventSoftAPModeStationConnected& event) {
powAttachInterrupts();
});
}
void powLoop() {
static unsigned long last_update = 0;
static unsigned char report_count = POW_REPORT_EVERY;
if ((millis() - last_update > POW_UPDATE_INTERVAL) || (last_update == 0 )){
last_update = millis();
unsigned int power = getActivePower();
char buffer[100];
sprintf_P(buffer, PSTR("{\"powVisible\": 1, \"powActivePower\": %d}"), power);
webSocketSend(buffer);
if (--report_count == 0) {
mqttSend((char *) getSetting("powPowerTopic", POW_POWER_TOPIC).c_str(), (char *) String(power).c_str());
report_count = POW_REPORT_EVERY;
}
}
}
#endif

+ 1
- 1
code/src/version.h View File

@ -1,4 +1,4 @@
#define APP_NAME "Espurna"
#define APP_VERSION "0.9.8"
#define APP_VERSION "0.9.9"
#define APP_AUTHOR "xose.perez@gmail.com"
#define APP_WEBSITE "http://tinkerman.cat"

+ 17
- 0
code/src/websockets.ino View File

@ -67,6 +67,15 @@ void webSocketParse(uint8_t num, uint8_t * payload, size_t length) {
String key = config[i]["name"];
String value = config[i]["value"];
#if ENABLE_POW
if (key == "powExpectedPower") {
powSetExpectedActivePower(value.toInt());
continue;
}
#endif
if (key == "ssid") {
key = key + String(network);
}
@ -137,21 +146,29 @@ void webSocketStart(uint8_t num) {
root["relayMode"] = getSetting("relayMode", String(RELAY_MODE));
#if ENABLE_DHT
root["dhtVisible"] = 1;
root["dhtTmp"] = getTemperature();
root["dhtHum"] = getHumidity();
#endif
#if ENABLE_RF
root["rfVisible"] = 1;
root["rfChannel"] = getSetting("rfChannel", String(RF_CHANNEL));
root["rfDevice"] = getSetting("rfDevice", String(RF_DEVICE));
#endif
#if ENABLE_EMON
root["emonVisible"] = 1;
root["emonPower"] = getPower();
root["emonMains"] = getSetting("emonMains", String(EMON_MAINS_VOLTAGE));
root["emonRatio"] = getSetting("emonRatio", String(EMON_CURRENT_RATIO));
#endif
#if ENABLE_POW
root["powVisible"] = 1;
root["powActivePower"] = getActivePower();
#endif
JsonArray& wifi = root.createNestedArray("wifi");
for (byte i=0; i<3; i++) {
JsonObject& network = wifi.createNestedObject();


Loading…
Cancel
Save