Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

256 lines
6.3 KiB

  1. /*
  2. ESPurna
  3. RELAY MODULE
  4. Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com>
  5. */
  6. #include <EEPROM.h>
  7. #include <ArduinoJson.h>
  8. #include <vector>
  9. std::vector<unsigned char> _relays;
  10. bool recursive = false;
  11. #ifdef SONOFF_DUAL
  12. unsigned char dualRelayStatus = 0;
  13. #endif
  14. // -----------------------------------------------------------------------------
  15. // RELAY
  16. // -----------------------------------------------------------------------------
  17. void relayMQTT(unsigned char id) {
  18. if (id >= _relays.size()) return;
  19. String mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER);
  20. char buffer[strlen(MQTT_RELAY_TOPIC) + mqttGetter.length() + 3];
  21. sprintf(buffer, "%s/%d%s", MQTT_RELAY_TOPIC, id, mqttGetter.c_str());
  22. mqttSend(buffer, relayStatus(id) ? "1" : "0");
  23. }
  24. void relayMQTT() {
  25. for (unsigned int i=0; i < _relays.size(); i++) {
  26. relayMQTT(i);
  27. }
  28. }
  29. String relayString() {
  30. DynamicJsonBuffer jsonBuffer;
  31. JsonObject& root = jsonBuffer.createObject();
  32. JsonArray& relay = root.createNestedArray("relayStatus");
  33. for (unsigned char i=0; i<relayCount(); i++) {
  34. relay.add(relayStatus(i));
  35. }
  36. String output;
  37. root.printTo(output);
  38. return output;
  39. }
  40. void relayWS() {
  41. String output = relayString();
  42. wsSend((char *) output.c_str());
  43. }
  44. bool relayStatus(unsigned char id) {
  45. #ifdef SONOFF_DUAL
  46. if (id >= 2) return false;
  47. return ((dualRelayStatus & (1 << id)) > 0);
  48. #else
  49. if (id >= _relays.size()) return false;
  50. return (digitalRead(_relays[id]) == HIGH);
  51. #endif
  52. }
  53. bool relayStatus(unsigned char id, bool status, bool report) {
  54. if (id >= _relays.size()) return false;
  55. bool changed = false;
  56. if (relayStatus(id) != status) {
  57. DEBUG_MSG("[RELAY] %d => %s\n", id, status ? "ON" : "OFF");
  58. changed = true;
  59. #ifdef SONOFF_DUAL
  60. dualRelayStatus ^= (1 << id);
  61. Serial.flush();
  62. Serial.write(0xA0);
  63. Serial.write(0x04);
  64. Serial.write(dualRelayStatus);
  65. Serial.write(0xA1);
  66. Serial.flush();
  67. #else
  68. digitalWrite(_relays[id], status);
  69. #endif
  70. if (!recursive) {
  71. relaySync(id);
  72. relaySave();
  73. }
  74. #ifdef ENABLE_DOMOTICZ
  75. domoticzSend(id);
  76. #endif
  77. }
  78. if (report) relayMQTT(id);
  79. if (!recursive) relayWS();
  80. return changed;
  81. }
  82. bool relayStatus(unsigned char id, bool status) {
  83. if (id >= _relays.size()) return false;
  84. return relayStatus(id, status, true);
  85. }
  86. void relaySync(unsigned char id) {
  87. if (_relays.size() > 1) {
  88. recursive = true;
  89. byte relaySync = getSetting("relaySync", RELAY_SYNC).toInt();
  90. bool status = relayStatus(id);
  91. // If RELAY_SYNC_SAME all relays should have the same state
  92. if (relaySync == RELAY_SYNC_SAME) {
  93. for (unsigned short i=0; i<_relays.size(); i++) {
  94. if (i != id) relayStatus(i, status);
  95. }
  96. // If NONE_OR_ONE or ONE and setting ON we should set OFF all the others
  97. } else if (status) {
  98. if (relaySync != RELAY_SYNC_ANY) {
  99. for (unsigned short i=0; i<_relays.size(); i++) {
  100. if (i != id) relayStatus(i, false);
  101. }
  102. }
  103. // If ONLY_ONE and setting OFF we should set ON the other one
  104. } else {
  105. if (relaySync == RELAY_SYNC_ONE) {
  106. unsigned char i = (id + 1) % _relays.size();
  107. relayStatus(i, true);
  108. }
  109. }
  110. recursive = false;
  111. }
  112. }
  113. void relaySave() {
  114. unsigned char bit = 1;
  115. unsigned char mask = 0;
  116. for (unsigned int i=0; i < _relays.size(); i++) {
  117. if (relayStatus(i)) mask += bit;
  118. bit += bit;
  119. }
  120. EEPROM.write(0, mask);
  121. EEPROM.commit();
  122. }
  123. void relayRetrieve() {
  124. recursive = true;
  125. unsigned char bit = 1;
  126. unsigned char mask = EEPROM.read(0);
  127. for (unsigned int i=0; i < _relays.size(); i++) {
  128. relayStatus(i, ((mask & bit) == bit));
  129. bit += bit;
  130. }
  131. recursive = false;
  132. }
  133. void relayToggle(unsigned char id) {
  134. if (id >= _relays.size()) return;
  135. relayStatus(id, !relayStatus(id));
  136. }
  137. unsigned char relayCount() {
  138. return _relays.size();
  139. }
  140. void relayMQTTCallback(unsigned int type, const char * topic, const char * payload) {
  141. String mqttSetter = getSetting("mqttSetter", MQTT_USE_SETTER);
  142. String mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER);
  143. bool sameSetGet = mqttGetter.compareTo(mqttSetter) == 0;
  144. if (type == MQTT_CONNECT_EVENT) {
  145. relayMQTT();
  146. char buffer[strlen(MQTT_RELAY_TOPIC) + mqttSetter.length() + 3];
  147. sprintf(buffer, "%s/+%s", MQTT_RELAY_TOPIC, mqttSetter.c_str());
  148. mqttSubscribe(buffer);
  149. }
  150. if (type == MQTT_MESSAGE_EVENT) {
  151. // Match topic
  152. String t = String(topic + mqttTopicRootLength());
  153. if (!t.startsWith(MQTT_RELAY_TOPIC)) return;
  154. if (!t.endsWith(mqttSetter)) return;
  155. // Get relay ID
  156. unsigned int relayID = topic[strlen(MQTT_RELAY_TOPIC)+1] - '0';
  157. if (relayID >= relayCount()) relayID = 0;
  158. // Action to perform
  159. unsigned int value = (char)payload[0] - '0';
  160. if (value == 2) {
  161. relayToggle(relayID);
  162. } else {
  163. relayStatus(relayID, value > 0, !sameSetGet);
  164. }
  165. }
  166. }
  167. void relaySetup() {
  168. #ifdef SONOFF_DUAL
  169. // Two dummy relays for the dual
  170. _relays.push_back(0);
  171. _relays.push_back(0);
  172. #else
  173. #ifdef RELAY1_PIN
  174. _relays.push_back(RELAY1_PIN);
  175. #endif
  176. #ifdef RELAY2_PIN
  177. _relays.push_back(RELAY2_PIN);
  178. #endif
  179. #ifdef RELAY3_PIN
  180. _relays.push_back(RELAY3_PIN);
  181. #endif
  182. #ifdef RELAY4_PIN
  183. _relays.push_back(RELAY4_PIN);
  184. #endif
  185. #endif
  186. EEPROM.begin(4096);
  187. byte relayMode = getSetting("relayMode", RELAY_MODE).toInt();
  188. for (unsigned int i=0; i < _relays.size(); i++) {
  189. pinMode(_relays[i], OUTPUT);
  190. if (relayMode == RELAY_MODE_OFF) relayStatus(i, false);
  191. if (relayMode == RELAY_MODE_ON) relayStatus(i, true);
  192. }
  193. if (relayMode == RELAY_MODE_SAME) relayRetrieve();
  194. mqttRegister(relayMQTTCallback);
  195. DEBUG_MSG("[RELAY] Number of relays: %d\n", _relays.size());
  196. }