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.

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