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.

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