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.

902 lines
26 KiB

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