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.

809 lines
23 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. /*
  2. RELAY MODULE
  3. Copyright (C) 2016-2018 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #include <EEPROM.h>
  6. #include <Ticker.h>
  7. #include <ArduinoJson.h>
  8. #include <vector>
  9. #include <functional>
  10. typedef struct {
  11. // Configuration variables
  12. unsigned char pin; // GPIO pin for the relay
  13. unsigned char type; // RELAY_TYPE_NORMAL, RELAY_TYPE_INVERSE or RELAY_TYPE_LATCHED
  14. unsigned char reset_pin; // GPIO to reset the relay if RELAY_TYPE_LATCHED
  15. unsigned long delay_on; // Delay to turn relay ON
  16. unsigned long delay_off; // Delay to turn relay OFF
  17. unsigned char pulse; // RELAY_PULSE_NONE, RELAY_PULSE_OFF or RELAY_PULSE_ON
  18. unsigned long pulse_ms; // Pulse length in millis
  19. // Status variables
  20. bool current_status; // Holds the current (physical) status of the relay
  21. bool target_status; // Holds the target status
  22. unsigned long fw_start; // Flood window start time
  23. unsigned char fw_count; // Number of changes within the current flood window
  24. unsigned long change_time; // Scheduled time to change
  25. bool report; // Whether to report to own topic
  26. bool group_report; // Whether to report to group topic
  27. // Helping objects
  28. Ticker pulseTicker; // Holds the pulse back timer
  29. } relay_t;
  30. std::vector<relay_t> _relays;
  31. bool _relayRecursive = false;
  32. Ticker _relaySaveTicker;
  33. // -----------------------------------------------------------------------------
  34. // RELAY PROVIDERS
  35. // -----------------------------------------------------------------------------
  36. void _relayProviderStatus(unsigned char id, bool status) {
  37. // Check relay ID
  38. if (id >= _relays.size()) return;
  39. // Store new current status
  40. _relays[id].current_status = status;
  41. #if RELAY_PROVIDER == RELAY_PROVIDER_RFBRIDGE
  42. rfbStatus(id, status);
  43. #endif
  44. #if RELAY_PROVIDER == RELAY_PROVIDER_DUAL
  45. // Calculate mask
  46. unsigned char mask=0;
  47. for (unsigned char i=0; i<_relays.size(); i++) {
  48. if (_relays[i].current_status) mask = mask + (1 << i);
  49. }
  50. // Send it to F330
  51. Serial.flush();
  52. Serial.write(0xA0);
  53. Serial.write(0x04);
  54. Serial.write(mask);
  55. Serial.write(0xA1);
  56. Serial.flush();
  57. #endif
  58. #if RELAY_PROVIDER == RELAY_PROVIDER_LIGHT
  59. lightState(status);
  60. lightUpdate(true, true);
  61. #endif
  62. #if RELAY_PROVIDER == RELAY_PROVIDER_RELAY
  63. if (_relays[id].type == RELAY_TYPE_NORMAL) {
  64. digitalWrite(_relays[id].pin, status);
  65. } else if (_relays[id].type == RELAY_TYPE_INVERSE) {
  66. digitalWrite(_relays[id].pin, !status);
  67. } else if (_relays[id].type == RELAY_TYPE_LATCHED) {
  68. digitalWrite(_relays[id].pin, LOW);
  69. digitalWrite(_relays[id].reset_pin, LOW);
  70. if (status) {
  71. digitalWrite(_relays[id].pin, HIGH);
  72. } else {
  73. digitalWrite(_relays[id].reset_pin, HIGH);
  74. }
  75. delay(RELAY_LATCHING_PULSE);
  76. digitalWrite(_relays[id].pin, LOW);
  77. digitalWrite(_relays[id].reset_pin, LOW);
  78. }
  79. #endif
  80. }
  81. // -----------------------------------------------------------------------------
  82. // RELAY
  83. // -----------------------------------------------------------------------------
  84. void relayPulse(unsigned char id) {
  85. byte mode = _relays[id].pulse;
  86. if (mode == RELAY_PULSE_NONE) return;
  87. unsigned long ms = _relays[id].pulse_ms;
  88. if (ms == 0) return;
  89. bool status = relayStatus(id);
  90. bool pulseStatus = (mode == RELAY_PULSE_ON);
  91. if (pulseStatus == status) {
  92. _relays[id].pulseTicker.detach();
  93. } else {
  94. _relays[id].pulseTicker.once_ms(ms, relayToggle, id);
  95. }
  96. }
  97. bool relayStatus(unsigned char id, bool status, bool report, bool group_report) {
  98. if (id >= _relays.size()) return false;
  99. bool changed = false;
  100. if (_relays[id].current_status == status) {
  101. if (_relays[id].target_status != status) {
  102. DEBUG_MSG_P(PSTR("[RELAY] #%d scheduled change cancelled\n"), id);
  103. _relays[id].target_status = status;
  104. _relays[id].report = false;
  105. _relays[id].group_report = false;
  106. changed = true;
  107. }
  108. // For RFBridge, keep sending the message even if the status is already the required
  109. #if RELAY_PROVIDER == RELAY_PROVIDER_RFBRIDGE
  110. rfbStatus(id, status);
  111. #endif
  112. // Update the pulse counter if the relay is already in the non-normal state (#454)
  113. relayPulse(id);
  114. } else {
  115. unsigned int current_time = millis();
  116. unsigned int fw_end = _relays[id].fw_start + 1000 * RELAY_FLOOD_WINDOW;
  117. unsigned long delay = status ? _relays[id].delay_on : _relays[id].delay_off;
  118. _relays[id].fw_count++;
  119. _relays[id].change_time = current_time + delay;
  120. // If current_time is off-limits the floodWindow...
  121. if (current_time < _relays[id].fw_start || fw_end <= current_time) {
  122. // We reset the floodWindow
  123. _relays[id].fw_start = current_time;
  124. _relays[id].fw_count = 1;
  125. // If current_time is in the floodWindow and there have been too many requests...
  126. } else if (_relays[id].fw_count >= RELAY_FLOOD_CHANGES) {
  127. // We schedule the changes to the end of the floodWindow
  128. // unless it's already delayed beyond that point
  129. if (fw_end - delay > current_time) {
  130. _relays[id].change_time = fw_end;
  131. }
  132. }
  133. _relays[id].target_status = status;
  134. if (report) _relays[id].report = true;
  135. if (group_report) _relays[id].group_report = true;
  136. relaySync(id);
  137. DEBUG_MSG_P(PSTR("[RELAY] #%d scheduled %s in %u ms\n"),
  138. id, status ? "ON" : "OFF",
  139. (_relays[id].change_time - current_time));
  140. changed = true;
  141. }
  142. return changed;
  143. }
  144. bool relayStatus(unsigned char id, bool status) {
  145. return relayStatus(id, status, true, true);
  146. }
  147. bool relayStatus(unsigned char id) {
  148. // Check relay ID
  149. if (id >= _relays.size()) return false;
  150. // Get status from storage
  151. return _relays[id].current_status;
  152. }
  153. void relaySync(unsigned char id) {
  154. // No sync if none or only one relay
  155. if (_relays.size() < 2) return;
  156. // Do not go on if we are comming from a previous sync
  157. if (_relayRecursive) return;
  158. // Flag sync mode
  159. _relayRecursive = true;
  160. byte relaySync = getSetting("relaySync", RELAY_SYNC).toInt();
  161. bool status = _relays[id].target_status;
  162. // If RELAY_SYNC_SAME all relays should have the same state
  163. if (relaySync == RELAY_SYNC_SAME) {
  164. for (unsigned short i=0; i<_relays.size(); i++) {
  165. if (i != id) relayStatus(i, status);
  166. }
  167. // If NONE_OR_ONE or ONE and setting ON we should set OFF all the others
  168. } else if (status) {
  169. if (relaySync != RELAY_SYNC_ANY) {
  170. for (unsigned short i=0; i<_relays.size(); i++) {
  171. if (i != id) relayStatus(i, false);
  172. }
  173. }
  174. // If ONLY_ONE and setting OFF we should set ON the other one
  175. } else {
  176. if (relaySync == RELAY_SYNC_ONE) {
  177. unsigned char i = (id + 1) % _relays.size();
  178. relayStatus(i, true);
  179. }
  180. }
  181. // Unflag sync mode
  182. _relayRecursive = false;
  183. }
  184. void relaySave() {
  185. unsigned char bit = 1;
  186. unsigned char mask = 0;
  187. for (unsigned int i=0; i < _relays.size(); i++) {
  188. if (relayStatus(i)) mask += bit;
  189. bit += bit;
  190. }
  191. EEPROM.write(EEPROM_RELAY_STATUS, mask);
  192. DEBUG_MSG_P(PSTR("[RELAY] Saving mask: %d\n"), mask);
  193. EEPROM.commit();
  194. }
  195. void relayToggle(unsigned char id, bool report, bool group_report) {
  196. if (id >= _relays.size()) return;
  197. relayStatus(id, !relayStatus(id), report, group_report);
  198. }
  199. void relayToggle(unsigned char id) {
  200. relayToggle(id, true, true);
  201. }
  202. unsigned char relayCount() {
  203. return _relays.size();
  204. }
  205. unsigned char relayParsePayload(const char * payload) {
  206. // Payload could be "OFF", "ON", "TOGGLE"
  207. // or its number equivalents: 0, 1 or 2
  208. if (payload[0] == '0') return 0;
  209. if (payload[0] == '1') return 1;
  210. if (payload[0] == '2') return 2;
  211. // trim payload
  212. char * p = ltrim((char *)payload);
  213. // to lower
  214. unsigned int l = strlen(p);
  215. if (l>6) l=6;
  216. for (unsigned char i=0; i<l; i++) {
  217. p[i] = tolower(p[i]);
  218. }
  219. unsigned int value = 0xFF;
  220. if (strcmp(p, "off") == 0) {
  221. value = 0;
  222. } else if (strcmp(p, "on") == 0) {
  223. value = 1;
  224. } else if (strcmp(p, "toggle") == 0) {
  225. value = 2;
  226. } else if (strcmp(p, "query") == 0) {
  227. value = 3;
  228. }
  229. return value;
  230. }
  231. // BACKWARDS COMPATIBILITY
  232. void _relayBackwards() {
  233. byte relayMode = getSetting("relayMode", RELAY_BOOT_MODE).toInt();
  234. byte relayPulseMode = getSetting("relayPulseMode", RELAY_PULSE_MODE).toInt();
  235. float relayPulseTime = getSetting("relayPulseTime", RELAY_PULSE_TIME).toFloat();
  236. if (relayPulseMode == RELAY_PULSE_NONE) relayPulseTime = 0;
  237. for (unsigned int i=0; i<_relays.size(); i++) {
  238. if (!hasSetting("relayBoot", i)) setSetting("relayBoot", i, relayMode);
  239. if (!hasSetting("relayPulse", i)) setSetting("relayPulse", i, relayPulseMode);
  240. if (!hasSetting("relayTime", i)) setSetting("relayTime", i, relayPulseTime);
  241. }
  242. delSetting("relayMode");
  243. delSetting("relayPulseMode");
  244. delSetting("relayPulseTime");
  245. }
  246. void _relayBoot() {
  247. _relayRecursive = true;
  248. unsigned char bit = 1;
  249. bool trigger_save = false;
  250. // Get last statuses from EEPROM
  251. unsigned char mask = EEPROM.read(EEPROM_RELAY_STATUS);
  252. DEBUG_MSG_P(PSTR("[RELAY] Retrieving mask: %d\n"), mask);
  253. // Walk the relays
  254. bool status = false;
  255. for (unsigned int i=0; i<_relays.size(); i++) {
  256. unsigned char boot_mode = getSetting("relayBoot", i, RELAY_BOOT_MODE).toInt();
  257. DEBUG_MSG_P(PSTR("[RELAY] Relay #%d boot mode %d\n"), i, boot_mode);
  258. switch (boot_mode) {
  259. case RELAY_BOOT_SAME:
  260. status = ((mask & bit) == bit);
  261. break;
  262. case RELAY_BOOT_TOGGLE:
  263. status = ((mask & bit) != bit);
  264. mask ^= bit;
  265. trigger_save = true;
  266. break;
  267. case RELAY_BOOT_ON:
  268. status = true;
  269. break;
  270. case RELAY_BOOT_OFF:
  271. default:
  272. status = false;
  273. break;
  274. }
  275. _relays[i].current_status = !status;
  276. _relays[i].target_status = status;
  277. _relays[i].change_time = millis();
  278. bit <<= 1;
  279. }
  280. // Save if there is any relay in the RELAY_BOOT_TOGGLE mode
  281. if (trigger_save) {
  282. EEPROM.write(EEPROM_RELAY_STATUS, mask);
  283. EEPROM.commit();
  284. }
  285. _relayRecursive = false;
  286. }
  287. void _relayConfigure() {
  288. for (unsigned int i=0; i<_relays.size(); i++) {
  289. pinMode(_relays[i].pin, OUTPUT);
  290. if (_relays[i].type == RELAY_TYPE_LATCHED) pinMode(_relays[i].reset_pin, OUTPUT);
  291. _relays[i].pulse = getSetting("relayPulse", i, RELAY_PULSE_MODE).toInt();
  292. _relays[i].pulse_ms = 1000 * getSetting("relayTime", i, RELAY_PULSE_MODE).toFloat();
  293. }
  294. }
  295. //------------------------------------------------------------------------------
  296. // WEBSOCKETS
  297. //------------------------------------------------------------------------------
  298. #if WEB_SUPPORT
  299. void _relayWebSocketUpdate(JsonObject& root) {
  300. JsonArray& relay = root.createNestedArray("relayStatus");
  301. for (unsigned char i=0; i<relayCount(); i++) {
  302. relay.add(_relays[i].target_status);
  303. }
  304. }
  305. void _relayWebSocketOnStart(JsonObject& root) {
  306. if (relayCount() == 0) return;
  307. // Statuses
  308. _relayWebSocketUpdate(root);
  309. // Configuration
  310. JsonArray& config = root.createNestedArray("relayConfig");
  311. for (unsigned char i=0; i<relayCount(); i++) {
  312. JsonObject& line = config.createNestedObject();
  313. line["gpio"] = _relays[i].pin;
  314. line["type"] = _relays[i].type;
  315. line["reset"] = _relays[i].reset_pin;
  316. line["boot"] = getSetting("relayBoot", i, RELAY_BOOT_MODE).toInt();
  317. line["pulse"] = _relays[i].pulse;
  318. line["pulse_ms"] = _relays[i].pulse_ms / 1000.0;
  319. #if MQTT_SUPPORT
  320. line["group"] = getSetting("mqttGroup", i, "");
  321. line["group_inv"] = getSetting("mqttGroupInv", i, 0).toInt();
  322. #endif
  323. }
  324. if (relayCount() > 1) {
  325. root["multirelayVisible"] = 1;
  326. root["relaySync"] = getSetting("relaySync", RELAY_SYNC);
  327. }
  328. root["relayVisible"] = 1;
  329. }
  330. void _relayWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
  331. if (strcmp(action, "relay") != 0) return;
  332. if (data.containsKey("status")) {
  333. unsigned char value = relayParsePayload(data["status"]);
  334. if (value == 3) {
  335. wsSend(_relayWebSocketUpdate);
  336. } else if (value < 3) {
  337. unsigned int relayID = 0;
  338. if (data.containsKey("id")) {
  339. String value = data["id"];
  340. relayID = value.toInt();
  341. }
  342. // Action to perform
  343. if (value == 0) {
  344. relayStatus(relayID, false);
  345. } else if (value == 1) {
  346. relayStatus(relayID, true);
  347. } else if (value == 2) {
  348. relayToggle(relayID);
  349. }
  350. }
  351. }
  352. }
  353. void relaySetupWS() {
  354. wsOnSendRegister(_relayWebSocketOnStart);
  355. wsOnActionRegister(_relayWebSocketOnAction);
  356. wsOnAfterParseRegister(_relayConfigure);
  357. }
  358. #endif // WEB_SUPPORT
  359. //------------------------------------------------------------------------------
  360. // REST API
  361. //------------------------------------------------------------------------------
  362. #if WEB_SUPPORT
  363. void relaySetupAPI() {
  364. // API entry points (protected with apikey)
  365. for (unsigned int relayID=0; relayID<relayCount(); relayID++) {
  366. char key[15];
  367. snprintf_P(key, sizeof(key), PSTR("%s/%d"), MQTT_TOPIC_RELAY, relayID);
  368. apiRegister(key,
  369. [relayID](char * buffer, size_t len) {
  370. snprintf_P(buffer, len, PSTR("%d"), relayStatus(relayID) ? 1 : 0);
  371. },
  372. [relayID](const char * payload) {
  373. unsigned char value = relayParsePayload(payload);
  374. if (value == 0xFF) {
  375. DEBUG_MSG_P(PSTR("[RELAY] Wrong payload (%s)\n"), payload);
  376. return;
  377. }
  378. if (value == 0) {
  379. relayStatus(relayID, false);
  380. } else if (value == 1) {
  381. relayStatus(relayID, true);
  382. } else if (value == 2) {
  383. relayToggle(relayID);
  384. }
  385. }
  386. );
  387. }
  388. }
  389. #endif // WEB_SUPPORT
  390. //------------------------------------------------------------------------------
  391. // MQTT
  392. //------------------------------------------------------------------------------
  393. #if MQTT_SUPPORT
  394. void relayMQTT(unsigned char id) {
  395. if (id >= _relays.size()) return;
  396. // Send state topic
  397. if (_relays[id].report) {
  398. _relays[id].report = false;
  399. mqttSend(MQTT_TOPIC_RELAY, id, _relays[id].current_status ? "1" : "0");
  400. }
  401. // Check group topic
  402. if (_relays[id].group_report) {
  403. _relays[id].group_report = false;
  404. String t = getSetting("mqttGroup", id, "");
  405. if (t.length() > 0) {
  406. bool status = relayStatus(id);
  407. if (getSetting("mqttGroupInv", id, 0).toInt() == 1) status = !status;
  408. mqttSendRaw(t.c_str(), status ? "1" : "0");
  409. }
  410. }
  411. }
  412. void relayMQTT() {
  413. for (unsigned int id=0; id < _relays.size(); id++) {
  414. mqttSend(MQTT_TOPIC_RELAY, id, _relays[id].current_status ? "1" : "0");
  415. }
  416. }
  417. void relayStatusWrap(unsigned char id, unsigned char value, bool is_group_topic) {
  418. // Action to perform
  419. if (value == 0) {
  420. relayStatus(id, false, mqttForward(), !is_group_topic);
  421. } else if (value == 1) {
  422. relayStatus(id, true, mqttForward(), !is_group_topic);
  423. } else if (value == 2) {
  424. relayToggle(id, true, true);
  425. }
  426. }
  427. void relayMQTTCallback(unsigned int type, const char * topic, const char * payload) {
  428. if (type == MQTT_CONNECT_EVENT) {
  429. // Send status on connect
  430. #if not HEARTBEAT_REPORT_RELAY
  431. relayMQTT();
  432. #endif
  433. // Subscribe to own /set topic
  434. char buffer[strlen(MQTT_TOPIC_RELAY) + 3];
  435. snprintf_P(buffer, sizeof(buffer), PSTR("%s/+"), MQTT_TOPIC_RELAY);
  436. mqttSubscribe(buffer);
  437. // Subscribe to group topics
  438. for (unsigned int i=0; i < _relays.size(); i++) {
  439. String t = getSetting("mqttGroup", i, "");
  440. if (t.length() > 0) mqttSubscribeRaw(t.c_str());
  441. }
  442. }
  443. if (type == MQTT_MESSAGE_EVENT) {
  444. // Check relay topic
  445. String t = mqttTopicKey((char *) topic);
  446. if (t.startsWith(MQTT_TOPIC_RELAY)) {
  447. // Get value
  448. unsigned char value = relayParsePayload(payload);
  449. if (value == 0xFF) return;
  450. // Get relay ID
  451. unsigned int id = t.substring(strlen(MQTT_TOPIC_RELAY)+1).toInt();
  452. if (id >= relayCount()) {
  453. DEBUG_MSG_P(PSTR("[RELAY] Wrong relayID (%d)\n"), id);
  454. } else {
  455. relayStatusWrap(id, value, false);
  456. }
  457. return;
  458. }
  459. // Check group topics
  460. for (unsigned int i=0; i < _relays.size(); i++) {
  461. String t = getSetting("mqttGroup", i, "");
  462. if ((t.length() > 0) && t.equals(topic)) {
  463. unsigned char value = relayParsePayload(payload);
  464. if (value == 0xFF) return;
  465. if (value < 2) {
  466. if (getSetting("mqttGroupInv", i, 0).toInt() == 1) {
  467. value = 1 - value;
  468. }
  469. }
  470. DEBUG_MSG_P(PSTR("[RELAY] Matched group topic for relayID %d\n"), i);
  471. relayStatusWrap(i, value, true);
  472. }
  473. }
  474. }
  475. }
  476. void relaySetupMQTT() {
  477. mqttRegister(relayMQTTCallback);
  478. }
  479. #endif
  480. //------------------------------------------------------------------------------
  481. // InfluxDB
  482. //------------------------------------------------------------------------------
  483. #if INFLUXDB_SUPPORT
  484. void relayInfluxDB(unsigned char id) {
  485. if (id >= _relays.size()) return;
  486. idbSend(MQTT_TOPIC_RELAY, id, relayStatus(id) ? "1" : "0");
  487. }
  488. #endif
  489. //------------------------------------------------------------------------------
  490. // Settings
  491. //------------------------------------------------------------------------------
  492. #if TERMINAL_SUPPORT
  493. void _relayInitCommands() {
  494. settingsRegisterCommand(F("RELAY"), [](Embedis* e) {
  495. if (e->argc < 2) {
  496. DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n"));
  497. }
  498. int id = String(e->argv[1]).toInt();
  499. if (e->argc > 2) {
  500. int value = String(e->argv[2]).toInt();
  501. if (value == 2) {
  502. relayToggle(id);
  503. } else {
  504. relayStatus(id, value == 1);
  505. }
  506. }
  507. DEBUG_MSG_P(PSTR("Status: %s\n"), relayStatus(id) ? "true" : "false");
  508. DEBUG_MSG_P(PSTR("+OK\n"));
  509. });
  510. }
  511. #endif // TERMINAL_SUPPORT
  512. //------------------------------------------------------------------------------
  513. // Setup
  514. //------------------------------------------------------------------------------
  515. void relaySetup() {
  516. // Dummy relays for AI Light, Magic Home LED Controller, H801,
  517. // Sonoff Dual and Sonoff RF Bridge
  518. #ifdef DUMMY_RELAY_COUNT
  519. for (unsigned char i=0; i < DUMMY_RELAY_COUNT; i++) {
  520. _relays.push_back((relay_t) {0, RELAY_TYPE_NORMAL});
  521. }
  522. #else
  523. #ifdef RELAY1_PIN
  524. _relays.push_back((relay_t) { RELAY1_PIN, RELAY1_TYPE, RELAY1_RESET_PIN, RELAY1_DELAY_ON, RELAY1_DELAY_OFF });
  525. #endif
  526. #ifdef RELAY2_PIN
  527. _relays.push_back((relay_t) { RELAY2_PIN, RELAY2_TYPE, RELAY2_RESET_PIN, RELAY2_DELAY_ON, RELAY2_DELAY_OFF });
  528. #endif
  529. #ifdef RELAY3_PIN
  530. _relays.push_back((relay_t) { RELAY3_PIN, RELAY3_TYPE, RELAY3_RESET_PIN, RELAY3_DELAY_ON, RELAY3_DELAY_OFF });
  531. #endif
  532. #ifdef RELAY4_PIN
  533. _relays.push_back((relay_t) { RELAY4_PIN, RELAY4_TYPE, RELAY4_RESET_PIN, RELAY4_DELAY_ON, RELAY4_DELAY_OFF });
  534. #endif
  535. #ifdef RELAY5_PIN
  536. _relays.push_back((relay_t) { RELAY5_PIN, RELAY5_TYPE, RELAY5_RESET_PIN, RELAY5_DELAY_ON, RELAY5_DELAY_OFF });
  537. #endif
  538. #ifdef RELAY6_PIN
  539. _relays.push_back((relay_t) { RELAY6_PIN, RELAY6_TYPE, RELAY6_RESET_PIN, RELAY6_DELAY_ON, RELAY6_DELAY_OFF });
  540. #endif
  541. #ifdef RELAY7_PIN
  542. _relays.push_back((relay_t) { RELAY7_PIN, RELAY7_TYPE, RELAY7_RESET_PIN, RELAY7_DELAY_ON, RELAY7_DELAY_OFF });
  543. #endif
  544. #ifdef RELAY8_PIN
  545. _relays.push_back((relay_t) { RELAY8_PIN, RELAY8_TYPE, RELAY8_RESET_PIN, RELAY8_DELAY_ON, RELAY8_DELAY_OFF });
  546. #endif
  547. #endif
  548. _relayBackwards();
  549. _relayConfigure();
  550. _relayBoot();
  551. relayLoop();
  552. espurnaRegisterLoop(relayLoop);
  553. #if WEB_SUPPORT
  554. relaySetupAPI();
  555. relaySetupWS();
  556. #endif
  557. #if MQTT_SUPPORT
  558. relaySetupMQTT();
  559. #endif
  560. #if TERMINAL_SUPPORT
  561. _relayInitCommands();
  562. #endif
  563. DEBUG_MSG_P(PSTR("[RELAY] Number of relays: %d\n"), _relays.size());
  564. }
  565. void relayLoop(void) {
  566. unsigned char id;
  567. for (id = 0; id < _relays.size(); id++) {
  568. unsigned int current_time = millis();
  569. bool status = _relays[id].target_status;
  570. if ((_relays[id].current_status != status)
  571. && (current_time >= _relays[id].change_time)) {
  572. DEBUG_MSG_P(PSTR("[RELAY] #%d set to %s\n"), id, status ? "ON" : "OFF");
  573. // Call the provider to perform the action
  574. _relayProviderStatus(id, status);
  575. // Send to Broker
  576. #if BROKER_SUPPORT
  577. brokerPublish(MQTT_TOPIC_RELAY, id, status ? "1" : "0");
  578. #endif
  579. // Send MQTT
  580. #if MQTT_SUPPORT
  581. relayMQTT(id);
  582. #endif
  583. if (!_relayRecursive) {
  584. relayPulse(id);
  585. _relaySaveTicker.once_ms(RELAY_SAVE_DELAY, relaySave);
  586. #if WEB_SUPPORT
  587. wsSend(_relayWebSocketUpdate);
  588. #endif
  589. }
  590. #if DOMOTICZ_SUPPORT
  591. domoticzSendRelay(id);
  592. #endif
  593. #if INFLUXDB_SUPPORT
  594. relayInfluxDB(id);
  595. #endif
  596. #if THINGSPEAK_SUPPORT
  597. tspkEnqueueRelay(id, status);
  598. tspkFlush();
  599. #endif
  600. // Flag relay-based LEDs to update status
  601. ledUpdate(true);
  602. _relays[id].report = false;
  603. _relays[id].group_report = false;
  604. }
  605. }
  606. }