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.

272 lines
6.4 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 <Ticker.h>
  8. #include <ArduinoJson.h>
  9. #include <vector>
  10. std::vector<unsigned char> _relays;
  11. bool recursive = false;
  12. #ifdef SONOFF_DUAL
  13. unsigned char dualRelayStatus = 0;
  14. #endif
  15. Ticker inching;
  16. // -----------------------------------------------------------------------------
  17. // RELAY
  18. // -----------------------------------------------------------------------------
  19. void relayMQTT(unsigned char id) {
  20. char buffer[10];
  21. sprintf(buffer, MQTT_RELAY_TOPIC, id);
  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. return ((dualRelayStatus & (1 << id)) > 0);
  47. #else
  48. return (digitalRead(_relays[id]) == HIGH);
  49. #endif
  50. }
  51. void relayInchingBack(unsigned char id) {
  52. relayToggle(id);
  53. inching.detach();
  54. }
  55. void relayInching(unsigned char id) {
  56. byte relayInch = getSetting("relayInch", String(RELAY_INCHING)).toInt();
  57. if (relayInch == RELAY_INCHING_NONE) return;
  58. bool status = relayStatus(id);
  59. bool inchingStatus = (relayInch == RELAY_INCHING_ON);
  60. if (inchingStatus == status) {
  61. inching.detach();
  62. return;
  63. }
  64. inching.attach(
  65. getSetting("relayInchTime", String(RELAY_INCHING_TIME)).toInt(),
  66. relayInchingBack,
  67. id
  68. );
  69. }
  70. bool relayStatus(unsigned char id, bool status, bool report) {
  71. bool changed = false;
  72. if (relayStatus(id) != status) {
  73. DEBUG_MSG("[RELAY] %d => %s\n", id, status ? "ON" : "OFF");
  74. changed = true;
  75. #ifdef SONOFF_DUAL
  76. dualRelayStatus ^= (1 << id);
  77. Serial.flush();
  78. Serial.write(0xA0);
  79. Serial.write(0x04);
  80. Serial.write(dualRelayStatus);
  81. Serial.write(0xA1);
  82. Serial.flush();
  83. #else
  84. digitalWrite(_relays[id], status);
  85. #endif
  86. if (!recursive) {
  87. relayInching(id);
  88. relaySync(id);
  89. relaySave();
  90. }
  91. }
  92. if (report) relayMQTT(id);
  93. if (!recursive) relayWS();
  94. return changed;
  95. }
  96. void relaySync(unsigned char id) {
  97. if (_relays.size() > 1) {
  98. recursive = true;
  99. byte relaySync = getSetting("relaySync", String(RELAY_SYNC)).toInt();
  100. bool status = relayStatus(id);
  101. // If RELAY_SYNC_SAME all relays should have the same state
  102. if (relaySync == RELAY_SYNC_SAME) {
  103. for (unsigned short i=0; i<_relays.size(); i++) {
  104. if (i != id) relayStatus(i, status);
  105. }
  106. // If NONE_OR_ONE or ONE and setting ON we should set OFF all the others
  107. } else if (status) {
  108. if (relaySync != RELAY_SYNC_ANY) {
  109. for (unsigned short i=0; i<_relays.size(); i++) {
  110. if (i != id) relayStatus(i, false);
  111. }
  112. }
  113. // If ONLY_ONE and setting OFF we should set ON the other one
  114. } else {
  115. if (relaySync == RELAY_SYNC_ONE) {
  116. unsigned char i = (id + 1) % _relays.size();
  117. relayStatus(i, true);
  118. }
  119. }
  120. recursive = false;
  121. }
  122. }
  123. void relaySave() {
  124. unsigned char bit = 1;
  125. unsigned char mask = 0;
  126. for (unsigned int i=0; i < _relays.size(); i++) {
  127. if (relayStatus(i)) mask += bit;
  128. bit += bit;
  129. }
  130. EEPROM.write(0, mask);
  131. EEPROM.commit();
  132. }
  133. void relayRetrieve() {
  134. recursive = true;
  135. unsigned char bit = 1;
  136. unsigned char mask = EEPROM.read(0);
  137. for (unsigned int i=0; i < _relays.size(); i++) {
  138. relayStatus(i, ((mask & bit) == bit));
  139. bit += bit;
  140. }
  141. recursive = false;
  142. }
  143. void relayToggle(unsigned char id) {
  144. relayStatus(id, !relayStatus(id));
  145. }
  146. unsigned char relayCount() {
  147. return _relays.size();
  148. }
  149. void relayMQTTCallback(unsigned int type, const char * topic, const char * payload) {
  150. static bool isFirstMessage = true;
  151. if (type == MQTT_CONNECT_EVENT) {
  152. relayMQTT();
  153. mqttSubscribe("/relay/#");
  154. }
  155. if (type == MQTT_MESSAGE_EVENT) {
  156. // Match topic
  157. if (memcmp("/relay/", topic, 7) != 0) return;
  158. // If relayMode is not SAME avoid responding to a retained message
  159. if (isFirstMessage) {
  160. isFirstMessage = false;
  161. byte relayMode = getSetting("relayMode", String(RELAY_MODE)).toInt();
  162. if (relayMode != RELAY_MODE_SAME) return;
  163. }
  164. // Get relay ID
  165. unsigned int relayID = topic[strlen(topic)-1] - '0';
  166. if (relayID >= relayCount()) relayID = 0;
  167. // Action to perform
  168. if ((char)payload[0] == '0') {
  169. relayStatus(relayID, false, false);
  170. }
  171. if ((char)payload[0] == '1') {
  172. relayStatus(relayID, true, false);
  173. }
  174. if ((char)payload[0] == '2') {
  175. relayToggle(relayID);
  176. }
  177. }
  178. }
  179. void relaySetup() {
  180. #ifdef SONOFF_DUAL
  181. // Two dummy relays for the dual
  182. _relays.push_back(0);
  183. _relays.push_back(0);
  184. #else
  185. #ifdef RELAY1_PIN
  186. _relays.push_back(RELAY1_PIN);
  187. #endif
  188. #ifdef RELAY2_PIN
  189. _relays.push_back(RELAY2_PIN);
  190. #endif
  191. #ifdef RELAY3_PIN
  192. _relays.push_back(RELAY3_PIN);
  193. #endif
  194. #ifdef RELAY4_PIN
  195. _relays.push_back(RELAY4_PIN);
  196. #endif
  197. #endif
  198. EEPROM.begin(4096);
  199. byte relayMode = getSetting("relayMode", String(RELAY_MODE)).toInt();
  200. for (unsigned int i=0; i < _relays.size(); i++) {
  201. pinMode(_relays[i], OUTPUT);
  202. if (relayMode == RELAY_MODE_OFF) relayStatus(i, false);
  203. if (relayMode == RELAY_MODE_ON) relayStatus(i, true);
  204. }
  205. if (relayMode == RELAY_MODE_SAME) relayRetrieve();
  206. mqttRegister(relayMQTTCallback);
  207. DEBUG_MSG("[RELAY] Number of relays: %d\n", _relays.size());
  208. }