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.

790 lines
21 KiB

6 years ago
  1. /*
  2. RF MODULE
  3. Copyright (C) 2016-2018 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if defined(ITEAD_SONOFF_RFBRIDGE) || RF_SUPPORT
  6. #include <queue>
  7. #include <Ticker.h>
  8. #if RFB_DIRECT || RF_SUPPORT
  9. #include <RCSwitch.h>
  10. #endif
  11. // -----------------------------------------------------------------------------
  12. // DEFINITIONS
  13. // -----------------------------------------------------------------------------
  14. // EFM8 Protocol
  15. #define RF_MESSAGE_SIZE 9
  16. #define RF_MAX_MESSAGE_SIZE (112+4)
  17. #define RF_CODE_START 0xAA
  18. #define RF_CODE_ACK 0xA0
  19. #define RF_CODE_LEARN 0xA1
  20. #define RF_CODE_LEARN_KO 0xA2
  21. #define RF_CODE_LEARN_OK 0xA3
  22. #define RF_CODE_RFIN 0xA4
  23. #define RF_CODE_RFOUT 0xA5
  24. #define RF_CODE_SNIFFING_ON 0xA6
  25. #define RF_CODE_SNIFFING_OFF 0xA7
  26. #define RF_CODE_RFOUT_NEW 0xA8
  27. #define RF_CODE_LEARN_NEW 0xA9
  28. #define RF_CODE_LEARN_KO_NEW 0xAA
  29. #define RF_CODE_LEARN_OK_NEW 0xAB
  30. #define RF_CODE_RFOUT_BUCKET 0xB0
  31. #define RF_CODE_STOP 0x55
  32. // Settings
  33. #define RF_MAX_KEY_LENGTH (9)
  34. // -----------------------------------------------------------------------------
  35. // GLOBALS TO THE MODULE
  36. // -----------------------------------------------------------------------------
  37. unsigned char _uartbuf[RF_MESSAGE_SIZE+3] = {0};
  38. unsigned char _uartpos = 0;
  39. unsigned char _learnId = 0;
  40. bool _learnStatus = true;
  41. bool _rfbin = false;
  42. #if not RF_SUPPORT
  43. typedef struct {
  44. byte code[RF_MESSAGE_SIZE];
  45. byte times;
  46. } rfb_message_t;
  47. static std::queue<rfb_message_t> _rfb_message_queue;
  48. Ticker _rfb_ticker;
  49. bool _rfb_ticker_active = false;
  50. #endif
  51. #if RFB_DIRECT || RF_SUPPORT
  52. RCSwitch * _rfModem;
  53. bool _learning = false;
  54. #endif
  55. #if WEB_SUPPORT
  56. Ticker _rfb_sendcodes;
  57. #endif
  58. // -----------------------------------------------------------------------------
  59. // PRIVATES
  60. // -----------------------------------------------------------------------------
  61. /*
  62. From a byte array to an hexa char array ("A220EE...", double the size)
  63. */
  64. static bool _rfbToChar(byte * in, char * out, int n = RF_MESSAGE_SIZE) {
  65. for (unsigned char p = 0; p<n; p++) {
  66. sprintf_P(&out[p*2], PSTR("%02X"), in[p]);
  67. }
  68. return true;
  69. }
  70. #if WEB_SUPPORT
  71. void _rfbWebSocketSendCodes() {
  72. DynamicJsonBuffer jsonBuffer;
  73. JsonObject& root = jsonBuffer.createObject();
  74. root["size"] = relayCount();
  75. JsonArray& on = root.createNestedArray("on");
  76. JsonArray& off = root.createNestedArray("off");
  77. for (byte id=0; id<relayCount(); id++) {
  78. on.add(rfbRetrieve(id, true));
  79. off.add(rfbRetrieve(id, false));
  80. }
  81. wsSend(rfb);
  82. }
  83. void _rfbWebSocketOnSend(JsonObject& root) {
  84. root["rfbVisible"] = 1;
  85. root["rfbCount"] = relayCount();
  86. #if RF_RAW_SUPPORT
  87. root["rfbrawVisible"] = 1;
  88. #endif
  89. _rfb_sendcodes.once_ms(1000, _rfbWebSocketSendCodes);
  90. }
  91. void _rfbWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
  92. if (strcmp(action, "rfblearn") == 0) rfbLearn(data["id"], data["status"]);
  93. if (strcmp(action, "rfbforget") == 0) rfbForget(data["id"], data["status"]);
  94. if (strcmp(action, "rfbsend") == 0) rfbStore(data["id"], data["status"], data["data"].as<const char*>());
  95. }
  96. #endif // WEB_SUPPORT
  97. void _rfbAck() {
  98. #if (not RFB_DIRECT) && (not RF_SUPPORT)
  99. DEBUG_MSG_P(PSTR("[RF] Sending ACK\n"));
  100. Serial.println();
  101. Serial.write(RF_CODE_START);
  102. Serial.write(RF_CODE_ACK);
  103. Serial.write(RF_CODE_STOP);
  104. Serial.flush();
  105. Serial.println();
  106. #endif
  107. }
  108. void _rfbLearn() {
  109. #if RFB_DIRECT || RF_SUPPORT
  110. DEBUG_MSG_P(PSTR("[RF] Entering LEARN mode\n"));
  111. _learning = true;
  112. #else
  113. DEBUG_MSG_P(PSTR("[RF] Sending LEARN\n"));
  114. Serial.println();
  115. Serial.write(RF_CODE_START);
  116. Serial.write(RF_CODE_LEARN);
  117. Serial.write(RF_CODE_STOP);
  118. Serial.flush();
  119. Serial.println();
  120. #endif
  121. #if WEB_SUPPORT
  122. char buffer[100];
  123. snprintf_P(buffer, sizeof(buffer), PSTR("{\"action\": \"rfbLearn\", \"data\":{\"id\": %d, \"status\": %d}}"), _learnId, _learnStatus ? 1 : 0);
  124. wsSend(buffer);
  125. #endif
  126. }
  127. /*
  128. From an hexa char array ("A220EE...") to a byte array (half the size)
  129. */
  130. static int _rfbToArray(const char * in, byte * out, int length = RF_MESSAGE_SIZE * 2) {
  131. int n = strlen(in);
  132. if (n > RF_MAX_MESSAGE_SIZE*2 || (length > 0 && n != length)) return 0;
  133. char tmp[3] = {0,0,0};
  134. n /= 2;
  135. for (unsigned char p = 0; p<n; p++) {
  136. memcpy(tmp, &in[p*2], 2);
  137. out[p] = strtol(tmp, NULL, 16);
  138. }
  139. return n;
  140. }
  141. #if not RF_SUPPORT
  142. void _rfbSendRaw(const byte *message, const unsigned char n = RF_MESSAGE_SIZE) {
  143. for (unsigned char j=0; j<n; j++) {
  144. Serial.write(message[j]);
  145. }
  146. }
  147. void _rfbSend(byte * message) {
  148. #if RFB_DIRECT
  149. unsigned int protocol = message[1];
  150. unsigned int timing =
  151. (message[2] << 8) |
  152. (message[3] << 0) ;
  153. unsigned int bitlength = message[4];
  154. unsigned long rf_code =
  155. (message[5] << 24) |
  156. (message[6] << 16) |
  157. (message[7] << 8) |
  158. (message[8] << 0) ;
  159. _rfModem->setProtocol(protocol);
  160. if (timing > 0) {
  161. _rfModem->setPulseLength(timing);
  162. }
  163. _rfModem->send(rf_code, bitlength);
  164. _rfModem->resetAvailable();
  165. #else
  166. Serial.println();
  167. Serial.write(RF_CODE_START);
  168. Serial.write(RF_CODE_RFOUT);
  169. _rfbSendRaw(message);
  170. Serial.write(RF_CODE_STOP);
  171. Serial.flush();
  172. Serial.println();
  173. #endif
  174. }
  175. void _rfbSend() {
  176. // Check if there is something in the queue
  177. if (_rfb_message_queue.empty()) return;
  178. // Pop the first element
  179. rfb_message_t message = _rfb_message_queue.front();
  180. _rfb_message_queue.pop();
  181. // Send the message
  182. _rfbSend(message.code);
  183. // If it should be further sent, push it to the stack again
  184. if (message.times > 1) {
  185. message.times = message.times - 1;
  186. _rfb_message_queue.push(message);
  187. }
  188. // if there are still messages in the queue...
  189. if (_rfb_message_queue.empty()) {
  190. _rfb_ticker.detach();
  191. _rfb_ticker_active = false;
  192. }
  193. }
  194. void _rfbSend(byte * code, unsigned char times) {
  195. #if RFB_DIRECT
  196. times = 1;
  197. #endif
  198. char buffer[RF_MESSAGE_SIZE];
  199. _rfbToChar(code, buffer);
  200. DEBUG_MSG_P(PSTR("[RF] Enqueuing MESSAGE '%s' %d time(s)\n"), buffer, times);
  201. rfb_message_t message;
  202. memcpy(message.code, code, RF_MESSAGE_SIZE);
  203. message.times = times;
  204. _rfb_message_queue.push(message);
  205. // Enable the ticker if not running
  206. if (!_rfb_ticker_active) {
  207. _rfb_ticker_active = true;
  208. _rfb_ticker.attach_ms(RF_SEND_DELAY, _rfbSend);
  209. }
  210. }
  211. #endif // not RF_SUPPORT
  212. #if RF_RAW_SUPPORT
  213. void _rfbSendRawOnce(byte *code, unsigned char length) {
  214. char buffer[length*2];
  215. _rfbToChar(code, buffer, length);
  216. DEBUG_MSG_P(PSTR("[RF] Sending RAW MESSAGE '%s'\n"), buffer);
  217. _rfbSendRaw(code, length);
  218. }
  219. #endif // RF_RAW_SUPPORT
  220. bool _rfbMatch(char* code, unsigned char& relayID, unsigned char& value, char* buffer = NULL) {
  221. if (strlen(code) != 18) return false;
  222. bool found = false;
  223. String compareto = String(&code[12]);
  224. compareto.toUpperCase();
  225. DEBUG_MSG_P(PSTR("[RF] Trying to match code %s\n"), compareto.c_str());
  226. for (unsigned char i=0; i<relayCount(); i++) {
  227. String code_on = rfbRetrieve(i, true);
  228. if (code_on.length() && code_on.endsWith(compareto)) {
  229. DEBUG_MSG_P(PSTR("[RF] Match ON code for relay %d\n"), i);
  230. value = 1;
  231. found = true;
  232. if (buffer) strcpy(buffer, code_on.c_str());
  233. }
  234. String code_off = rfbRetrieve(i, false);
  235. if (code_off.length() && code_off.endsWith(compareto)) {
  236. DEBUG_MSG_P(PSTR("[RF] Match OFF code for relay %d\n"), i);
  237. if (found) value = 2;
  238. found = true;
  239. if (buffer) strcpy(buffer, code_off.c_str());
  240. }
  241. if (found) {
  242. relayID = i;
  243. return true;
  244. }
  245. }
  246. return false;
  247. }
  248. void _rfbDecode() {
  249. static unsigned long last = 0;
  250. if (millis() - last < RF_RECEIVE_DELAY) return;
  251. last = millis();
  252. byte action = _uartbuf[0];
  253. char buffer[RF_MESSAGE_SIZE * 2 + 1] = {0};
  254. DEBUG_MSG_P(PSTR("[RF] Action 0x%02X\n"), action);
  255. if (action == RF_CODE_LEARN_KO) {
  256. _rfbAck();
  257. DEBUG_MSG_P(PSTR("[RF] Learn timeout\n"));
  258. #if WEB_SUPPORT
  259. wsSend_P(PSTR("{\"action\": \"rfbTimeout\"}"));
  260. #endif
  261. }
  262. if (action == RF_CODE_LEARN_OK || action == RF_CODE_RFIN) {
  263. _rfbAck();
  264. _rfbToChar(&_uartbuf[1], buffer);
  265. DEBUG_MSG_P(PSTR("[RF] Received message '%s'\n"), buffer);
  266. }
  267. if (action == RF_CODE_LEARN_OK) {
  268. DEBUG_MSG_P(PSTR("[RF] Learn success\n"));
  269. rfbStore(_learnId, _learnStatus, buffer);
  270. // Websocket update
  271. #if WEB_SUPPORT
  272. _rfbWebSocketSendCode(_learnId, _learnStatus, buffer);
  273. #endif
  274. }
  275. if (action == RF_CODE_RFIN) {
  276. /* Look for the code, possibly replacing the code with the exact learned one on match
  277. * we want to do this on learn too to be sure that the learned code is the same if it
  278. * is equivalent
  279. */
  280. unsigned char id;
  281. unsigned char status;
  282. bool matched = _rfbMatch(buffer, id, status, buffer);
  283. if (matched) {
  284. DEBUG_MSG_P(PSTR("[RF] Matched message '%s'\n"), buffer);
  285. _rfbin = true;
  286. if (status == 2) {
  287. relayToggle(id);
  288. } else {
  289. relayStatus(id, status == 1);
  290. }
  291. }
  292. #if MQTT_SUPPORT
  293. mqttSend(MQTT_TOPIC_RFIN, buffer);
  294. #endif
  295. }
  296. }
  297. void _rfbReceive() {
  298. #if RFB_DIRECT || RF_SUPPORT
  299. static long learn_start = 0;
  300. if (!_learning && learn_start) {
  301. learn_start = 0;
  302. }
  303. if (_learning) {
  304. if (!learn_start) {
  305. DEBUG_MSG_P(PSTR("[RF] Arming learn timeout\n"));
  306. learn_start = millis();
  307. }
  308. if (learn_start > 0 && millis() - learn_start > RF_LEARN_TIMEOUT) {
  309. DEBUG_MSG_P(PSTR("[RF] Learn timeout triggered\n"));
  310. memset(_uartbuf, 0, sizeof(_uartbuf));
  311. _uartbuf[0] = RF_CODE_LEARN_KO;
  312. _rfbDecode();
  313. _learning = false;
  314. }
  315. }
  316. if (_rfModem->available()) {
  317. static unsigned long last = 0;
  318. if (millis() - last > RF_DEBOUNCE) {
  319. last = millis();
  320. unsigned long rf_code = _rfModem->getReceivedValue();
  321. if ( rf_code > 0) {
  322. DEBUG_MSG_P(PSTR("[RF] Received code: %08X\n"), rf_code);
  323. unsigned int timing = _rfModem->getReceivedDelay();
  324. memset(_uartbuf, 0, sizeof(_uartbuf));
  325. unsigned char *msgbuf = _uartbuf + 1;
  326. _uartbuf[0] = _learning ? RF_CODE_LEARN_OK: RF_CODE_RFIN;
  327. msgbuf[0] = 0xC0;
  328. msgbuf[1] = _rfModem->getReceivedProtocol();
  329. msgbuf[2] = timing >> 8;
  330. msgbuf[3] = timing >> 0;
  331. msgbuf[4] = _rfModem->getReceivedBitlength();
  332. msgbuf[5] = rf_code >> 24;
  333. msgbuf[6] = rf_code >> 16;
  334. msgbuf[7] = rf_code >> 8;
  335. msgbuf[8] = rf_code >> 0;
  336. _rfbDecode();
  337. _learning = false;
  338. }
  339. }
  340. _rfModem->resetAvailable();
  341. }
  342. #else
  343. static bool receiving = false;
  344. while (Serial.available()) {
  345. yield();
  346. byte c = Serial.read();
  347. //DEBUG_MSG_P(PSTR("[RF] Received 0x%02X\n"), c);
  348. if (receiving) {
  349. if (c == RF_CODE_STOP && (_uartpos == 1 || _uartpos == RF_MESSAGE_SIZE + 1)) {
  350. _rfbDecode();
  351. receiving = false;
  352. } else if (_uartpos <= RF_MESSAGE_SIZE) {
  353. _uartbuf[_uartpos++] = c;
  354. } else {
  355. // wrong message, should have received a RF_CODE_STOP
  356. receiving = false;
  357. }
  358. } else if (c == RF_CODE_START) {
  359. _uartpos = 0;
  360. receiving = true;
  361. }
  362. }
  363. #endif
  364. }
  365. bool _rfbCompare(const char * code1, const char * code2) {
  366. return strcmp(&code1[12], &code2[12]) == 0;
  367. }
  368. bool _rfbSameOnOff(unsigned char id) {
  369. return _rfbCompare(rfbRetrieve(id, true).c_str(), rfbRetrieve(id, false).c_str());
  370. }
  371. void _rfbParseCode(char * code) {
  372. // The payload may be a code in HEX format ([0-9A-Z]{18}) or
  373. // the code comma the number of times to transmit it.
  374. char * tok = strtok(code, ",");
  375. // Check if a switch is linked to that message
  376. unsigned char id;
  377. unsigned char status = 0;
  378. if (_rfbMatch(tok, id, status)) {
  379. if (status == 2) {
  380. relayToggle(id);
  381. } else {
  382. relayStatus(id, status == 1);
  383. }
  384. return;
  385. }
  386. #if RF_RAW_SUPPORT
  387. byte message[RF_MAX_MESSAGE_SIZE];
  388. int len = _rfbToArray(tok, message, 0);
  389. if ((len > 0) && (isRFRaw || len != RF_MESSAGE_SIZE)) {
  390. _rfbSendRawOnce(message, len);
  391. } else {
  392. tok = strtok(NULL, ",");
  393. byte times = (tok != NULL) ? atoi(tok) : 1;
  394. _rfbSend(message, times);
  395. }
  396. #else // RF_RAW_SUPPORT
  397. byte message[RF_MESSAGE_SIZE];
  398. if (_rfbToArray(tok, message)) {
  399. tok = strtok(NULL, ",");
  400. byte times = (tok != NULL) ? atoi(tok) : 1;
  401. _rfbSend(message, times);
  402. }
  403. #endif // RF_RAW_SUPPORT
  404. }
  405. #if MQTT_SUPPORT
  406. void _rfbMqttCallback(unsigned int type, const char * topic, const char * payload) {
  407. if (type == MQTT_CONNECT_EVENT) {
  408. char buffer[strlen(MQTT_TOPIC_RFLEARN) + 3];
  409. snprintf_P(buffer, sizeof(buffer), PSTR("%s/+"), MQTT_TOPIC_RFLEARN);
  410. mqttSubscribe(buffer);
  411. #if not RF_SUPPORT
  412. mqttSubscribe(MQTT_TOPIC_RFOUT);
  413. #endif
  414. #if RF_RAW_SUPPORT
  415. mqttSubscribe(MQTT_TOPIC_RFRAW);
  416. #endif
  417. }
  418. if (type == MQTT_MESSAGE_EVENT) {
  419. // Match topic
  420. String t = mqttMagnitude((char *) topic);
  421. // Check if should go into learn mode
  422. if (t.startsWith(MQTT_TOPIC_RFLEARN)) {
  423. _learnId = t.substring(strlen(MQTT_TOPIC_RFLEARN)+1).toInt();
  424. if (_learnId >= relayCount()) {
  425. DEBUG_MSG_P(PSTR("[RF] Wrong learnID (%d)\n"), _learnId);
  426. return;
  427. }
  428. _learnStatus = (char)payload[0] != '0';
  429. _rfbLearn();
  430. return;
  431. }
  432. #if not RF_SUPPORT
  433. bool isRFOut = t.equals(MQTT_TOPIC_RFOUT);
  434. #endif
  435. #if RF_RAW_SUPPORT
  436. bool isRFRaw = !isRFOut && t.equals(MQTT_TOPIC_RFRAW);
  437. #elif not RF_SUPPORT
  438. bool isRFRaw = false;
  439. #endif
  440. #if not RF_SUPPORT
  441. if (isRFOut || isRFRaw) {
  442. _rfbParseCode((char *) payload);
  443. }
  444. #endif // not RF_SUPPORT
  445. }
  446. }
  447. #endif // MQTT_SUPPORT
  448. #if API_SUPPORT
  449. void _rfbAPISetup() {
  450. #if not RF_SUPPORT
  451. apiRegister(MQTT_TOPIC_RFOUT,
  452. [](char * buffer, size_t len) {
  453. snprintf_P(buffer, len, PSTR("OK"));
  454. },
  455. [](const char * payload) {
  456. _rfbParseCode((char *) payload);
  457. }
  458. );
  459. #endif // RF_SUPPORT
  460. apiRegister(MQTT_TOPIC_RFLEARN,
  461. [](char * buffer, size_t len) {
  462. snprintf_P(buffer, len, PSTR("OK"));
  463. },
  464. [](const char * payload) {
  465. // The payload must be the relayID plus the mode (0 or 1)
  466. char * tok = strtok((char *) payload, ",");
  467. if (NULL == tok) return;
  468. if (!isNumber(tok)) return;
  469. _learnId = atoi(tok);
  470. if (_learnId >= relayCount()) {
  471. DEBUG_MSG_P(PSTR("[RF] Wrong learnID (%d)\n"), _learnId);
  472. return;
  473. }
  474. tok = strtok(NULL, ",");
  475. if (NULL == tok) return;
  476. _learnStatus = (char) tok[0] != '0';
  477. _rfbLearn();
  478. }
  479. );
  480. #if RF_RAW_SUPPORT
  481. apiRegister(MQTT_TOPIC_RFRAW,
  482. [](char * buffer, size_t len) {
  483. snprintf_P(buffer, len, PSTR("OK"));
  484. },
  485. [](const char * payload) {
  486. _rfbParseCode(payload);
  487. }
  488. );
  489. #endif // RF_RAW_SUPPORT
  490. }
  491. #endif // API_SUPPORT
  492. #if TERMINAL_SUPPORT
  493. void _rfbInitCommands() {
  494. terminalRegisterCommand(F("LEARN"), [](Embedis* e) {
  495. if (e->argc < 3) {
  496. terminalError(F("Wrong arguments"));
  497. return;
  498. }
  499. int id = String(e->argv[1]).toInt();
  500. if (id >= relayCount()) {
  501. DEBUG_MSG_P(PSTR("-ERROR: Wrong relayID (%d)\n"), id);
  502. return;
  503. }
  504. int status = String(e->argv[2]).toInt();
  505. rfbLearn(id, status == 1);
  506. terminalOK();
  507. });
  508. terminalRegisterCommand(F("FORGET"), [](Embedis* e) {
  509. if (e->argc < 3) {
  510. terminalError(F("Wrong arguments"));
  511. return;
  512. }
  513. int id = String(e->argv[1]).toInt();
  514. if (id >= relayCount()) {
  515. DEBUG_MSG_P(PSTR("-ERROR: Wrong relayID (%d)\n"), id);
  516. return;
  517. }
  518. int status = String(e->argv[2]).toInt();
  519. rfbForget(id, status == 1);
  520. terminalOK();
  521. });
  522. }
  523. #endif // TERMINAL_SUPPORT
  524. // -----------------------------------------------------------------------------
  525. // PUBLIC
  526. // -----------------------------------------------------------------------------
  527. void rfbStore(unsigned char id, bool status, const char * code) {
  528. DEBUG_MSG_P(PSTR("[RF] Storing %d-%s => '%s'\n"), id, status ? "ON" : "OFF", code);
  529. char key[RF_MAX_KEY_LENGTH] = {0};
  530. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  531. setSetting(key, code);
  532. }
  533. String rfbRetrieve(unsigned char id, bool status) {
  534. char key[RF_MAX_KEY_LENGTH] = {0};
  535. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  536. return getSetting(key);
  537. }
  538. #if not RF_SUPPORT
  539. void rfbStatus(unsigned char id, bool status) {
  540. String value = rfbRetrieve(id, status);
  541. if (value.length() > 0) {
  542. bool same = _rfbSameOnOff(id);
  543. #if RF_RAW_SUPPORT
  544. byte message[RF_MAX_MESSAGE_SIZE];
  545. int len = _rfbToArray(value.c_str(), message, 0);
  546. if (len == RF_MESSAGE_SIZE && // probably a standard msg
  547. (message[0] != RF_CODE_START || // raw would start with 0xAA
  548. message[1] != RF_CODE_RFOUT_BUCKET || // followed by 0xB0,
  549. message[2] + 4 != len || // needs a valid length,
  550. message[len-1] != RF_CODE_STOP)) { // and finish with 0x55
  551. if (!_rfbin) {
  552. unsigned char times = same ? 1 : RF_SEND_TIMES;
  553. _rfbSend(message, times);
  554. }
  555. } else {
  556. _rfbSendRawOnce(message, len); // send a raw message
  557. }
  558. #else // RF_RAW_SUPPORT
  559. if (!_rfbin) {
  560. byte message[RF_MESSAGE_SIZE];
  561. _rfbToArray(value.c_str(), message);
  562. unsigned char times = same ? 1 : RF_SEND_TIMES;
  563. _rfbSend(message, times);
  564. }
  565. #endif // RF_RAW_SUPPORT
  566. }
  567. _rfbin = false;
  568. }
  569. #endif // not RF_SUPPORT
  570. void rfbLearn(unsigned char id, bool status) {
  571. _learnId = id;
  572. _learnStatus = status;
  573. _rfbLearn();
  574. }
  575. void rfbForget(unsigned char id, bool status) {
  576. char key[RF_MAX_KEY_LENGTH] = {0};
  577. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  578. delSetting(key);
  579. // Websocket update
  580. #if WEB_SUPPORT
  581. char wsb[100];
  582. snprintf_P(wsb, sizeof(wsb), PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"\"}]}"), id, status ? 1 : 0);
  583. wsSend(wsb);
  584. #endif
  585. }
  586. // -----------------------------------------------------------------------------
  587. // SETUP & LOOP
  588. // -----------------------------------------------------------------------------
  589. void rfbSetup() {
  590. #if MQTT_SUPPORT
  591. mqttRegister(_rfbMqttCallback);
  592. #endif
  593. #if API_SUPPORT
  594. _rfbAPISetup();
  595. #endif
  596. #if WEB_SUPPORT
  597. wsOnSendRegister(_rfbWebSocketOnSend);
  598. wsOnActionRegister(_rfbWebSocketOnAction);
  599. #endif
  600. #if TERMINAL_SUPPORT
  601. _rfbInitCommands();
  602. #endif
  603. #if RFB_DIRECT || RF_SUPPORT
  604. _rfModem = new RCSwitch();
  605. #if RF_SUPPORT
  606. _rfModem->enableReceive(RF_PIN);
  607. DEBUG_MSG_P(PSTR("[RF] RF receiver on GPIO %u\n"), RF_PIN);
  608. #else
  609. _rfModem->enableReceive(RFB_RX_PIN);
  610. _rfModem->enableTransmit(RFB_TX_PIN);
  611. _rfModem->setRepeatTransmit(6);
  612. DEBUG_MSG_P(PSTR("[RF] RF receiver on GPIO %u\n"), RFB_RX_PIN);
  613. DEBUG_MSG_P(PSTR("[RF] RF transmitter on GPIO %u\n"), RFB_TX_PIN);
  614. #endif
  615. #endif
  616. // Register loop
  617. espurnaRegisterLoop(rfbLoop);
  618. }
  619. void rfbLoop() {
  620. _rfbReceive();
  621. }
  622. #endif