Browse Source

Boot mode and pulse mode per relay

fastled
Xose Pérez 7 years ago
parent
commit
b3336871bf
8 changed files with 3274 additions and 3287 deletions
  1. +0
    -1
      code/espurna/button.ino
  2. +10
    -10
      code/espurna/config/general.h
  3. BIN
      code/espurna/data/index.html.gz
  4. +127
    -117
      code/espurna/relay.ino
  5. +3072
    -3074
      code/espurna/static/index.html.gz.h
  6. +1
    -1
      code/espurna/ws.ino
  7. +32
    -38
      code/html/custom.js
  8. +32
    -46
      code/html/index.html

+ 0
- 1
code/espurna/button.ino View File

@ -110,7 +110,6 @@ void buttonEvent(unsigned int id, unsigned char event) {
if (action == BUTTON_MODE_RESET) {
deferredReset(100, CUSTOM_RESET_HARDWARE);
}
if (action == BUTTON_MODE_PULSE) relayPulseToggle();
if (action == BUTTON_MODE_FACTORY) {
DEBUG_MSG_P(PSTR("\n\nFACTORY RESET\n\n"));
settingsFactoryReset();


+ 10
- 10
code/espurna/config/general.h View File

@ -196,10 +196,10 @@ PROGMEM const char* const custom_reset_string[] = {
// RELAY
//------------------------------------------------------------------------------
#define RELAY_MODE_OFF 0
#define RELAY_MODE_ON 1
#define RELAY_MODE_SAME 2
#define RELAY_MODE_TOOGLE 3
#define RELAY_BOOT_OFF 0
#define RELAY_BOOT_ON 1
#define RELAY_BOOT_SAME 2
#define RELAY_BOOT_TOOGLE 3
#define RELAY_TYPE_NORMAL 0
#define RELAY_TYPE_INVERSE 1
@ -219,18 +219,18 @@ PROGMEM const char* const custom_reset_string[] = {
#define RELAY_PROVIDER_LIGHT 2
#define RELAY_PROVIDER_RFBRIDGE 3
// Pulse time in milliseconds
#define RELAY_PULSE_TIME 1.0
// 0 means OFF, 1 ON and 2 whatever was before
#define RELAY_MODE RELAY_MODE_OFF
// Default boot mode: 0 means OFF, 1 ON and 2 whatever was before
#define RELAY_BOOT_MODE RELAY_BOOT_OFF
// 0 means ANY, 1 zero or one and 2 one and only one
#define RELAY_SYNC RELAY_SYNC_ANY
// 0 means no pulses, 1 means normally off, 2 normally on
// Default pulse mode: 0 means no pulses, 1 means normally off, 2 normally on
#define RELAY_PULSE_MODE RELAY_PULSE_NONE
// Default pulse time in seconds
#define RELAY_PULSE_TIME 1.0
// Relay requests flood protection window - in seconds
#define RELAY_FLOOD_WINDOW 3


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


+ 127
- 117
code/espurna/relay.ino View File

@ -17,24 +17,26 @@ typedef struct {
// Configuration variables
unsigned char pin; // GPIO pin for the relay
unsigned char type;
unsigned char reset_pin;
unsigned long delay_on;
unsigned long delay_off;
unsigned char type; // RELAY_TYPE_NORMAL, RELAY_TYPE_INVERSE or RELAY_TYPE_LATCHED
unsigned char reset_pin; // GPIO to reset the relay if RELAY_TYPE_LATCHED
unsigned long pulse; // RELAY_PULSE_NONE, RELAY_PULSE_OFF or RELAY_PULSE_ON
unsigned long pulse_ms; // Pulse length in millis
unsigned long delay_on; // Delay to turn relay ON
unsigned long delay_off; // Delay to turn relay OFF
// Status variables
bool current_status;
bool target_status;
unsigned int fw_start;
unsigned char fw_count;
unsigned int change_time;
bool report;
bool group_report;
bool current_status; // Holds the current (physical) status of the relay
bool target_status; // Holds the target status
unsigned int fw_start; // Flood window start time
unsigned char fw_count; // Number of changes within the current flood window
unsigned int change_time; // Scheduled time to change
bool report; // Whether to report to own topic
bool group_report; // Whether to report to group topic
// Helping objects
Ticker pulseTicker;
Ticker pulseTicker; // Holds the pulse back timer
} relay_t;
std::vector<relay_t> _relays;
@ -45,10 +47,6 @@ Ticker _relaySaveTicker;
// RELAY PROVIDERS
// -----------------------------------------------------------------------------
#if RELAY_PROVIDER == RELAY_PROVIDER_DUAL
#endif
void _relayProviderStatus(unsigned char id, bool status) {
// Check relay ID
@ -111,43 +109,20 @@ void _relayProviderStatus(unsigned char id, bool status) {
void relayPulse(unsigned char id) {
byte relayPulseMode = getSetting("relayPulseMode", RELAY_PULSE_MODE).toInt();
if (relayPulseMode == RELAY_PULSE_NONE) return;
long relayPulseTime = 1000.0 * getSetting("relayPulseTime", RELAY_PULSE_TIME).toFloat();
if (relayPulseTime == 0) return;
byte mode = _relays[id].pulse;
if (mode == RELAY_PULSE_NONE) return;
unsigned long ms = _relays[id].pulse_ms;
if (ms == 0) return;
bool status = relayStatus(id);
bool pulseStatus = (relayPulseMode == RELAY_PULSE_ON);
bool pulseStatus = (mode == RELAY_PULSE_ON);
if (pulseStatus == status) {
_relays[id].pulseTicker.detach();
return;
} else {
_relays[id].pulseTicker.once_ms(ms, relayToggle, id);
}
_relays[id].pulseTicker.once_ms(relayPulseTime, relayToggle, id);
}
unsigned int relayPulseMode() {
unsigned int value = getSetting("relayPulseMode", RELAY_PULSE_MODE).toInt();
return value;
}
void relayPulseMode(unsigned int value) {
setSetting("relayPulseMode", value);
#if WEB_SUPPORT
char message[20];
snprintf_P(message, sizeof(message), PSTR("{\"relayPulseMode\": %d}"), value);
wsSend(message);
#endif
}
void relayPulseToggle() {
unsigned int value = relayPulseMode();
value = (value == RELAY_PULSE_NONE) ? RELAY_PULSE_OFF : RELAY_PULSE_NONE;
relayPulseMode(value);
}
bool relayStatus(unsigned char id, bool status, bool report, bool group_report) {
@ -220,7 +195,7 @@ bool relayStatus(unsigned char id) {
// Check relay ID
if (id >= _relays.size()) return false;
// GEt status from storage
// Get status from storage
return _relays[id].current_status;
}
@ -278,24 +253,6 @@ void relaySave() {
EEPROM.commit();
}
void relayRetrieve(bool invert) {
_relayRecursive = true;
unsigned char bit = 1;
unsigned char mask = invert ? ~EEPROM.read(EEPROM_RELAY_STATUS) : EEPROM.read(EEPROM_RELAY_STATUS);
DEBUG_MSG_P(PSTR("[RELAY] Retrieving mask: %d\n"), mask);
for (unsigned int id=0; id < _relays.size(); id++) {
_relays[id].target_status = ((mask & bit) == bit);
_relays[id].report = true;
_relays[id].group_report = false; // Don't do group report on start
bit += bit;
}
if (invert) {
EEPROM.write(EEPROM_RELAY_STATUS, mask);
EEPROM.commit();
}
_relayRecursive = false;
}
void relayToggle(unsigned char id, bool report, bool group_report) {
if (id >= _relays.size()) return;
relayStatus(id, !relayStatus(id), report, group_report);
@ -341,59 +298,113 @@ unsigned char relayParsePayload(const char * payload) {
}
// BACKWARDS COMPATIBILITY
void _relayBackwards() {
byte relayMode = getSetting("relayMode", RELAY_BOOT_MODE).toInt();
byte relayPulseMode = getSetting("relayPulseMode", RELAY_PULSE_MODE).toInt();
float relayPulseTime = getSetting("relayPulseTime", RELAY_PULSE_TIME).toFloat();
if (relayPulseMode == RELAY_PULSE_NONE) relayPulseTime = 0;
for (unsigned int i=0; i<_relays.size(); i++) {
if (!hasSetting("relayBoot", i)) setSetting("relayBoot", i, relayMode);
if (!hasSetting("relayPulse", i)) setSetting("relayPulse", i, relayPulseMode);
if (!hasSetting("relayTime", i)) setSetting("relayTime", i, relayPulseTime);
}
delSetting("relayMode");
delSetting("relayPulseMode");
delSetting("relayPulseTime");
}
void _relayBoot() {
_relayRecursive = true;
unsigned char bit = 1;
bool trigger_save = false;
// Get last statuses from EEPROM
unsigned char mask = EEPROM.read(EEPROM_RELAY_STATUS);
DEBUG_MSG_P(PSTR("[RELAY] Retrieving mask: %d\n"), mask);
// Walk the relays
for (unsigned int i=0; i<_relays.size(); i++) {
_relays[i].current_status = false;
_relays[i].target_status = false;
unsigned char boot_mode = getSetting("relayBoot", i, RELAY_BOOT_MODE).toInt();
switch (boot_mode) {
case RELAY_BOOT_OFF:
relayStatus(i, false);
break;
case RELAY_BOOT_ON:
relayStatus(i, true);
break;
case RELAY_BOOT_SAME:
relayStatus(i, (mask & bit) == bit);
break;
case RELAY_BOOT_TOOGLE:
relayStatus(i, (mask & bit) != bit);
mask ^= bit;
trigger_save = true;
break;
}
bit <<= 1;
}
// Save if there is any relay in the RELAY_BOOT_TOOGLE mode
if (trigger_save) {
EEPROM.write(EEPROM_RELAY_STATUS, mask);
EEPROM.commit();
}
_relayRecursive = false;
}
//------------------------------------------------------------------------------
// WEBSOCKETS
//------------------------------------------------------------------------------
#if WEB_SUPPORT
void _relayWebSocketUpdate() {
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
// Statuses
void _relayWebSocketUpdate(JsonObject& root) {
JsonArray& relay = root.createNestedArray("relayStatus");
for (unsigned char i=0; i<relayCount(); i++) {
relay.add(_relays[i].target_status);
}
String output;
root.printTo(output);
wsSend((char *) output.c_str());
}
void _relayWebSocketOnSend(JsonObject& root) {
void _relayWebSocketOnStart(JsonObject& root) {
if (relayCount() == 0) return;
root["relayVisible"] = 1;
// Statuses
JsonArray& relay = root.createNestedArray("relayStatus");
for (unsigned char relayID=0; relayID<relayCount(); relayID++) {
relay.add(relayStatus(relayID));
}
_relayWebSocketUpdate(root);
// Configuration
root["relayMode"] = getSetting("relayMode", RELAY_MODE);
root["relayPulseMode"] = getSetting("relayPulseMode", RELAY_PULSE_MODE);
root["relayPulseTime"] = getSetting("relayPulseTime", RELAY_PULSE_TIME).toFloat();
JsonArray& config = root.createNestedArray("relayConfig");
for (unsigned char i=0; i<relayCount(); i++) {
JsonObject& line = config.createNestedObject();
line["gpio"] = _relays[i].pin;
line["type"] = _relays[i].type;
line["reset"] = _relays[i].reset_pin;
line["boot"] = getSetting("relayBoot", i, RELAY_BOOT_MODE).toInt();
line["pulse"] = _relays[i].pulse;
line["pulse_ms"] = _relays[i].pulse_ms / 1000.0;
#if MQTT_SUPPORT
line["group"] = getSetting("mqttGroup", i, "");
line["group_inv"] = getSetting("mqttGroupInv", i, 0).toInt() == 1;
#endif
}
if (relayCount() > 1) {
root["multirelayVisible"] = 1;
root["relaySync"] = getSetting("relaySync", RELAY_SYNC);
}
// Group topics
#if MQTT_SUPPORT
JsonArray& groups = root.createNestedArray("relayGroups");
for (unsigned char i=0; i<relayCount(); i++) {
JsonObject& group = groups.createNestedObject();
group["mqttGroup"] = getSetting("mqttGroup", i, "");
group["mqttGroupInv"] = getSetting("mqttGroupInv", i, 0).toInt() == 1;
}
#endif
root["relayVisible"] = 1;
}
@ -407,7 +418,7 @@ void _relayWebSocketOnAction(const char * action, JsonObject& data) {
if (value == 3) {
_relayWebSocketUpdate();
wsSend(_relayWebSocketUpdate);
} else if (value < 3) {
@ -432,9 +443,19 @@ void _relayWebSocketOnAction(const char * action, JsonObject& data) {
}
void _relayConfigure() {
for (unsigned int i=0; i<_relays.size(); i++) {
pinMode(_relays[i].pin, OUTPUT);
if (_relays[i].type == RELAY_TYPE_LATCHED) pinMode(_relays[i].reset_pin, OUTPUT);
_relays[i].pulse = getSetting("relayPulse", i, RELAY_PULSE_MODE).toInt();
_relays[i].pulse_ms = 1000 * getSetting("relayTime", i, RELAY_PULSE_MODE).toFloat();
}
}
void relaySetupWS() {
wsOnSendRegister(_relayWebSocketOnSend);
wsOnSendRegister(_relayWebSocketOnStart);
wsOnActionRegister(_relayWebSocketOnAction);
wsOnAfterParseRegister(_relayConfigure);
}
#endif // WEB_SUPPORT
@ -560,12 +581,12 @@ void relayMQTTCallback(unsigned int type, const char * topic, const char * paylo
for (unsigned int i=0; i < _relays.size(); i++) {
String t = getSetting("mqttGroup", i, "");
if (t.equals(topic)) {
is_group_topic = true;
relayID = i;
if (getSetting("mqttGroupInv", relayID, 0).toInt() == 1) {
if (getSetting("mqttGroupInv", i, 0).toInt() == 1) {
if (value < 2) value = 1 - value;
}
DEBUG_MSG_P(PSTR("[RELAY] Matched group topic for relayID %d\n"), relayID);
DEBUG_MSG_P(PSTR("[RELAY] Matched group topic for relayID %d\n"), i);
is_group_topic = true;
relayID = i;
break;
}
}
@ -577,12 +598,6 @@ void relayMQTTCallback(unsigned int type, const char * topic, const char * paylo
String t = mqttSubtopic((char *) topic);
if (!t.startsWith(MQTT_TOPIC_RELAY)) return;
// Pulse topic
if (t.endsWith("pulse")) {
relayPulseMode(value);
return;
}
// Get relay ID
relayID = t.substring(strlen(MQTT_TOPIC_RELAY)+1).toInt();
if (relayID >= relayCount()) {
@ -655,14 +670,9 @@ void relaySetup() {
#endif
byte relayMode = getSetting("relayMode", RELAY_MODE).toInt();
for (unsigned int i=0; i < _relays.size(); i++) {
pinMode(_relays[i].pin, OUTPUT);
if (relayMode == RELAY_MODE_OFF) relayStatus(i, false);
if (relayMode == RELAY_MODE_ON) relayStatus(i, true);
}
if (relayMode == RELAY_MODE_SAME) relayRetrieve(false);
if (relayMode == RELAY_MODE_TOOGLE) relayRetrieve(true);
_relayBackwards();
_relayConfigure();
_relayBoot();
relayLoop();
#if WEB_SUPPORT
@ -704,7 +714,7 @@ void relayLoop(void) {
relayPulse(id);
_relaySaveTicker.once_ms(RELAY_SAVE_DELAY, relaySave);
#if WEB_SUPPORT
_relayWebSocketUpdate();
wsSend(_relayWebSocketUpdate);
#endif
}


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


+ 1
- 1
code/espurna/ws.ino View File

@ -146,8 +146,8 @@ void _wsParse(AsyncWebSocketClient *client, uint8_t * payload, size_t length) {
if (password.length() > 0) {
setSetting(key, password);
save = true;
wsSend_P(client_id, PSTR("{\"action\": \"reload\"}"));
}
wsSend_P(client_id, PSTR("{\"action\": \"reload\"}"));
} else {
wsSend_P(client_id, PSTR("{\"message\": 7}"));
}


+ 32
- 38
code/html/custom.js View File

@ -63,7 +63,15 @@ function validateForm(form) {
}
// These fields will always be a list of values
var is_group = ["ssid", "pass", "gw", "mask", "ip", "dns", "mqttGroup", "mqttGroupInv", "dczRelayIdx", "ledMode", "ntpServer", "adminPass"];
var is_group = [
"ssid", "pass", "gw", "mask", "ip", "dns",
"relayBoot", "relayPulse", "relayTime",
"mqttGroup", "mqttGroupInv",
"dczRelayIdx",
"ledMode",
"ntpServer",
"adminPass"
];
function getData(form) {
@ -373,6 +381,26 @@ function createRelays(count) {
}
function initRelayConfig(data) {
var current = $("#relayConfig > div").length;
if (current > 0) return;
var template = $("#relayConfigTemplate").children();
for (var i=0; i < data.length; i++) {
var line = $(template).clone();
$("span.gpio", line).html(data[i].gpio);
$("span.id", line).html(i+1);
$("select[name='relayBoot']", line).val(data[i].boot);
$("select[name='relayPulse']", line).val(data[i].pulse);
$("input[name='relayTime']", line).val(data[i].pulse_ms);
$("intut[name='mqttGroup']", line).val(data[i].group || 0);
$("select[name='mqttGroupInv']", line).val(data[i].group_inv || 0);
line.appendTo("#relayConfig");
}
}
function createIdxs(count) {
var current = $("#idxs > div").length;
@ -422,23 +450,6 @@ function addNetwork() {
}
function addRelayGroup() {
var numGroups = $("#relayGroups > div").length;
var tabindex = 200 + numGroups * 2;
var template = $("#relayGroupTemplate").children();
var line = $(template).clone();
var element = $("span.relay_id", line);
if (element.length) element.html(numGroups+1);
$(line).find("input").each(function() {
$(this).attr("tabindex", tabindex++);
});
line.appendTo("#relayGroups");
return line;
}
function initColorRGB() {
// check if already initialized
@ -728,26 +739,9 @@ function processData(data) {
}
// Relay groups
if (key == "relayGroups") {
var groups = data.relayGroups;
for (var i in groups) {
// add a new row
var line = addRelayGroup();
// fill in the blanks
var group = data.relayGroups[i];
Object.keys(group).forEach(function(key) {
var element = $("input[name=" + key + "]", line);
if (element.length) element.val(group[key]);
});
}
return;
// Relay configuration
if (key == "relayConfig") {
initRelayConfig(data[key]);
}
// Domoticz


+ 32
- 46
code/html/index.html View File

@ -392,19 +392,7 @@
<fieldset>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4">Switch boot mode</label>
<div class="pure-u-1 pure-u-md-3-4">
<select name="relayMode" class="pure-u-3-4" tabindex="2">
<option value="0">Always OFF</option>
<option value="1">Always ON</option>
<option value="2">Same as before</option>
<option value="3">Toogle before</option>
</select>
</div>
<div class="pure-u-0 pure-u-md-1-4">&nbsp;</div>
<div class="pure-u-1 pure-u-md-3-4 hint">Here you can define what will be the status of the switch after a reboot.</div>
</div>
<div class="pure-g module module-multirelay"><legend>General</legend></div>
<div class="pure-g module module-multirelay">
<label class="pure-u-1 pure-u-md-1-4">Switch sync mode</label>
@ -420,34 +408,7 @@
<div class="pure-u-1 pure-u-md-3-4 hint">Define how the different switches should be synchronized.</div>
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4">Switch pulse mode</label>
<div class="pure-u-1 pure-u-md-3-4">
<select name="relayPulseMode" class="pure-u-3-4" tabindex="4">
<option value="0">Don't pulse</option>
<option value="1">Normally OFF (stays on for a short time)</option>
<option value="2">Normally ON (stays off for a short time)</option>
</select>
</div>
<div class="pure-u-0 pure-u-md-1-4">&nbsp;</div>
<div class="pure-u-1 pure-u-md-3-4 hint">When pulse mode is enabled the switch will automatically switch back to its normal state after the pulse time (below).</div>
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4">Switch pulse time</label>
<input name="relayPulseTime" class="pure-u-1 pure-u-md-3-4" type="number" min="0" step="0.1" max="3600" tabindex="5" />
<div class="pure-u-0 pure-u-md-1-4">&nbsp;</div>
<div class="pure-u-1 pure-u-md-3-4 hint">Pulse time in seconds (maximum value is 3600s, i.e. 1 hour, accepts decimals).</div>
</div>
<div class="pure-g">
<legend>MQTT group topics</legend>
</div>
<div id="relayGroups" class="module module-mqtt">
</div>
<div id="relayConfig"></div>
</fieldset>
@ -1012,7 +973,7 @@
</div> <!-- layout -->
<!-- Templates -->
<!-- Templates -------------------------------------------------------->
<div id="rfbNodeTemplate" class="template">
@ -1095,11 +1056,36 @@
</div>
</div>
<div id="relayGroupTemplate" class="template">
<div id="relayConfigTemplate" class="template">
<div class="pure-g"><legend>Switch #<span class="id"></span> (GPIO<span class="gpio"></span>)</legend></div>
<div class="pure-g">
<div class="pure-u-1 pure-u-sm-1-4"><label>Switch <span class="relay_id"></span></label></div>
<div class="pure-u-1 pure-u-sm-1-2"><input class="pure-u-sm-23-24" name="mqttGroup" tabindex="0" data="0" /></div>
<select class="pure-u-1 pure-u-sm-1-4" name="mqttGroupInv">
<div class="pure-u-1 pure-u-sm-1-4"><label>Boot mode</label></div>
<select class="pure-u-1 pure-u-sm-3-4" name="relayBoot">
<option value="0">Always OFF</option>
<option value="1">Always ON</option>
<option value="2">Same as before</option>
<option value="3">Toogle before</option>
</select>
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-sm-1-4"><label>Pulse mode</label></div>
<select class="pure-u-1 pure-u-sm-3-4" name="relayPulse">
<option value="0">Don't pulse</option>
<option value="1">Normally OFF</option>
<option value="2">Normally ON</option>
</select>
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-sm-1-4"><label>Pulse time (s)</label></div>
<div class="pure-u-1 pure-u-sm-1-4"><input name="relayTime" class="pure-u-23-24" type="number" min="0" step="0.1" max="3600" /></div>
</div>
<div class="pure-g module module-mqtt">
<div class="pure-u-1 pure-u-sm-1-4"><label>MQTT group</label></div>
<div class="pure-u-1 pure-u-sm-3-4"><input class="pure-u-23-24" name="mqttGroup" tabindex="0" data="0" action="reconnect" /></div>
</div>
<div class="pure-g module module-mqtt">
<div class="pure-u-1 pure-u-sm-1-4"><label>MQTT group sync</label></div>
<select class="pure-u-1 pure-u-sm-3-4" name="mqttGroupInv">
<option value="0">Same</option>
<option value="1">Inverse</option>
</select>


Loading…
Cancel
Save