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.

261 lines
6.5 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, (char *) (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. }
  75. if (report) relayMQTT(id);
  76. if (!recursive) relayWS();
  77. return changed;
  78. }
  79. bool relayStatus(unsigned char id, bool status) {
  80. if (id >= _relays.size()) return false;
  81. return relayStatus(id, status, true);
  82. }
  83. void relaySync(unsigned char id) {
  84. if (_relays.size() > 1) {
  85. recursive = true;
  86. byte relaySync = getSetting("relaySync", RELAY_SYNC).toInt();
  87. bool status = relayStatus(id);
  88. // If RELAY_SYNC_SAME all relays should have the same state
  89. if (relaySync == RELAY_SYNC_SAME) {
  90. for (unsigned short i=0; i<_relays.size(); i++) {
  91. if (i != id) relayStatus(i, status);
  92. }
  93. // If NONE_OR_ONE or ONE and setting ON we should set OFF all the others
  94. } else if (status) {
  95. if (relaySync != RELAY_SYNC_ANY) {
  96. for (unsigned short i=0; i<_relays.size(); i++) {
  97. if (i != id) relayStatus(i, false);
  98. }
  99. }
  100. // If ONLY_ONE and setting OFF we should set ON the other one
  101. } else {
  102. if (relaySync == RELAY_SYNC_ONE) {
  103. unsigned char i = (id + 1) % _relays.size();
  104. relayStatus(i, true);
  105. }
  106. }
  107. recursive = false;
  108. }
  109. }
  110. void relaySave() {
  111. unsigned char bit = 1;
  112. unsigned char mask = 0;
  113. for (unsigned int i=0; i < _relays.size(); i++) {
  114. if (relayStatus(i)) mask += bit;
  115. bit += bit;
  116. }
  117. EEPROM.write(0, mask);
  118. EEPROM.commit();
  119. }
  120. void relayRetrieve() {
  121. recursive = true;
  122. unsigned char bit = 1;
  123. unsigned char mask = EEPROM.read(0);
  124. for (unsigned int i=0; i < _relays.size(); i++) {
  125. relayStatus(i, ((mask & bit) == bit));
  126. bit += bit;
  127. }
  128. recursive = false;
  129. }
  130. void relayToggle(unsigned char id) {
  131. if (id >= _relays.size()) return;
  132. relayStatus(id, !relayStatus(id));
  133. }
  134. unsigned char relayCount() {
  135. return _relays.size();
  136. }
  137. void relayMQTTCallback(unsigned int type, const char * topic, const char * payload) {
  138. static bool isFirstMessage = true;
  139. String mqttSetter = getSetting("mqttSetter", MQTT_USE_SETTER);
  140. String mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER);
  141. bool sameSetGet = mqttGetter.compareTo(mqttSetter) == 0;
  142. if (type == MQTT_CONNECT_EVENT) {
  143. relayMQTT();
  144. char buffer[strlen(MQTT_RELAY_TOPIC) + mqttSetter.length() + 3];
  145. sprintf(buffer, "%s/+%s", MQTT_RELAY_TOPIC, mqttSetter.c_str());
  146. mqttSubscribe(buffer);
  147. }
  148. if (type == MQTT_MESSAGE_EVENT) {
  149. // Match topic
  150. String t = String(topic);
  151. if (!t.startsWith(MQTT_RELAY_TOPIC)) return;
  152. if (!t.endsWith(mqttSetter)) return;
  153. // If relayMode is not SAME avoid responding to a retained message
  154. if (sameSetGet && isFirstMessage) {
  155. isFirstMessage = false;
  156. byte relayMode = getSetting("relayMode", RELAY_MODE).toInt();
  157. if (relayMode != RELAY_MODE_SAME) return;
  158. }
  159. // Get relay ID
  160. unsigned int relayID = topic[strlen(MQTT_RELAY_TOPIC)+1] - '0';
  161. if (relayID >= relayCount()) relayID = 0;
  162. // Action to perform
  163. unsigned int value = (char)payload[0] - '0';
  164. if (value == 2) {
  165. relayToggle(relayID);
  166. } else {
  167. relayStatus(relayID, value > 0, !sameSetGet);
  168. }
  169. }
  170. }
  171. void relaySetup() {
  172. #ifdef SONOFF_DUAL
  173. // Two dummy relays for the dual
  174. _relays.push_back(0);
  175. _relays.push_back(0);
  176. #else
  177. #ifdef RELAY1_PIN
  178. _relays.push_back(RELAY1_PIN);
  179. #endif
  180. #ifdef RELAY2_PIN
  181. _relays.push_back(RELAY2_PIN);
  182. #endif
  183. #ifdef RELAY3_PIN
  184. _relays.push_back(RELAY3_PIN);
  185. #endif
  186. #ifdef RELAY4_PIN
  187. _relays.push_back(RELAY4_PIN);
  188. #endif
  189. #endif
  190. EEPROM.begin(4096);
  191. byte relayMode = getSetting("relayMode", RELAY_MODE).toInt();
  192. for (unsigned int i=0; i < _relays.size(); i++) {
  193. pinMode(_relays[i], OUTPUT);
  194. if (relayMode == RELAY_MODE_OFF) relayStatus(i, false);
  195. if (relayMode == RELAY_MODE_ON) relayStatus(i, true);
  196. }
  197. if (relayMode == RELAY_MODE_SAME) relayRetrieve();
  198. mqttRegister(relayMQTTCallback);
  199. DEBUG_MSG("[RELAY] Number of relays: %d\n", _relays.size());
  200. }