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.

785 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. #if not RF_SUPPORT
  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. 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. apiRegister(MQTT_TOPIC_RFOUT,
  450. [](char * buffer, size_t len) {
  451. snprintf_P(buffer, len, PSTR("OK"));
  452. },
  453. [](const char * payload) {
  454. _rfbParseCode((char *) payload);
  455. }
  456. );
  457. apiRegister(MQTT_TOPIC_RFLEARN,
  458. [](char * buffer, size_t len) {
  459. snprintf_P(buffer, len, PSTR("OK"));
  460. },
  461. [](const char * payload) {
  462. // The payload must be the relayID plus the mode (0 or 1)
  463. char * tok = strtok((char *) payload, ",");
  464. if (NULL == tok) return;
  465. if (!isNumber(tok)) return;
  466. _learnId = atoi(tok);
  467. if (_learnId >= relayCount()) {
  468. DEBUG_MSG_P(PSTR("[RF] Wrong learnID (%d)\n"), _learnId);
  469. return;
  470. }
  471. tok = strtok(NULL, ",");
  472. if (NULL == tok) return;
  473. _learnStatus = (char) tok[0] != '0';
  474. _rfbLearn();
  475. }
  476. );
  477. #if RF_RAW_SUPPORT
  478. apiRegister(MQTT_TOPIC_RFRAW,
  479. [](char * buffer, size_t len) {
  480. snprintf_P(buffer, len, PSTR("OK"));
  481. },
  482. [](const char * payload) {
  483. _rfbParseCode(payload);
  484. }
  485. );
  486. #endif // RF_RAW_SUPPORT
  487. }
  488. #endif // API_SUPPORT
  489. #if TERMINAL_SUPPORT
  490. void _rfbInitCommands() {
  491. terminalRegisterCommand(F("LEARN"), [](Embedis* e) {
  492. if (e->argc < 3) {
  493. DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n"));
  494. return;
  495. }
  496. int id = String(e->argv[1]).toInt();
  497. if (id >= relayCount()) {
  498. DEBUG_MSG_P(PSTR("-ERROR: Wrong relayID (%d)\n"), id);
  499. return;
  500. }
  501. int status = String(e->argv[2]).toInt();
  502. rfbLearn(id, status == 1);
  503. DEBUG_MSG_P(PSTR("+OK\n"));
  504. });
  505. terminalRegisterCommand(F("FORGET"), [](Embedis* e) {
  506. if (e->argc < 3) {
  507. DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n"));
  508. return;
  509. }
  510. int id = String(e->argv[1]).toInt();
  511. if (id >= relayCount()) {
  512. DEBUG_MSG_P(PSTR("-ERROR: Wrong relayID (%d)\n"), id);
  513. return;
  514. }
  515. int status = String(e->argv[2]).toInt();
  516. rfbForget(id, status == 1);
  517. DEBUG_MSG_P(PSTR("+OK\n"));
  518. });
  519. }
  520. #endif // TERMINAL_SUPPORT
  521. // -----------------------------------------------------------------------------
  522. // PUBLIC
  523. // -----------------------------------------------------------------------------
  524. void rfbStore(unsigned char id, bool status, const char * code) {
  525. DEBUG_MSG_P(PSTR("[RF] Storing %d-%s => '%s'\n"), id, status ? "ON" : "OFF", code);
  526. char key[RF_MAX_KEY_LENGTH] = {0};
  527. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  528. setSetting(key, code);
  529. }
  530. String rfbRetrieve(unsigned char id, bool status) {
  531. char key[RF_MAX_KEY_LENGTH] = {0};
  532. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  533. return getSetting(key);
  534. }
  535. #if not RF_SUPPORT
  536. void rfbStatus(unsigned char id, bool status) {
  537. String value = rfbRetrieve(id, status);
  538. if (value.length() > 0) {
  539. bool same = _rfbSameOnOff(id);
  540. #if RF_RAW_SUPPORT
  541. byte message[RF_MAX_MESSAGE_SIZE];
  542. int len = _rfbToArray(value.c_str(), message, 0);
  543. if (len == RF_MESSAGE_SIZE && // probably a standard msg
  544. (message[0] != RF_CODE_START || // raw would start with 0xAA
  545. message[1] != RF_CODE_RFOUT_BUCKET || // followed by 0xB0,
  546. message[2] + 4 != len || // needs a valid length,
  547. message[len-1] != RF_CODE_STOP)) { // and finish with 0x55
  548. if (!_rfbin) {
  549. unsigned char times = same ? 1 : RF_SEND_TIMES;
  550. _rfbSend(message, times);
  551. }
  552. } else {
  553. _rfbSendRawOnce(message, len); // send a raw message
  554. }
  555. #else // RF_RAW_SUPPORT
  556. if (!_rfbin) {
  557. byte message[RF_MESSAGE_SIZE];
  558. _rfbToArray(value.c_str(), message);
  559. unsigned char times = same ? 1 : RF_SEND_TIMES;
  560. _rfbSend(message, times);
  561. }
  562. #endif // RF_RAW_SUPPORT
  563. }
  564. _rfbin = false;
  565. }
  566. #endif // not RF_SUPPORT
  567. void rfbLearn(unsigned char id, bool status) {
  568. _learnId = id;
  569. _learnStatus = status;
  570. _rfbLearn();
  571. }
  572. void rfbForget(unsigned char id, bool status) {
  573. char key[RF_MAX_KEY_LENGTH] = {0};
  574. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  575. delSetting(key);
  576. // Websocket update
  577. #if WEB_SUPPORT
  578. char wsb[100];
  579. snprintf_P(wsb, sizeof(wsb), PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"\"}]}"), id, status ? 1 : 0);
  580. wsSend(wsb);
  581. #endif
  582. }
  583. // -----------------------------------------------------------------------------
  584. // SETUP & LOOP
  585. // -----------------------------------------------------------------------------
  586. void rfbSetup() {
  587. #if MQTT_SUPPORT
  588. mqttRegister(_rfbMqttCallback);
  589. #endif
  590. #if API_SUPPORT
  591. _rfbAPISetup();
  592. #endif
  593. #if WEB_SUPPORT
  594. wsOnSendRegister(_rfbWebSocketOnSend);
  595. wsOnActionRegister(_rfbWebSocketOnAction);
  596. #endif
  597. #if TERMINAL_SUPPORT
  598. _rfbInitCommands();
  599. #endif
  600. #if RFB_DIRECT || RF_SUPPORT
  601. _rfModem = new RCSwitch();
  602. #if RF_SUPPORT
  603. _rfModem->enableReceive(RF_PIN);
  604. DEBUG_MSG_P(PSTR("[RF] RF receiver on GPIO %u\n"), RF_PIN);
  605. #else
  606. _rfModem->enableReceive(RFB_RX_PIN);
  607. _rfModem->enableTransmit(RFB_TX_PIN);
  608. _rfModem->setRepeatTransmit(6);
  609. DEBUG_MSG_P(PSTR("[RF] RF receiver on GPIO %u\n"), RFB_RX_PIN);
  610. DEBUG_MSG_P(PSTR("[RF] RF transmitter on GPIO %u\n"), RFB_TX_PIN);
  611. #endif
  612. #endif
  613. // Register loop
  614. espurnaRegisterLoop(rfbLoop);
  615. }
  616. void rfbLoop() {
  617. _rfbReceive();
  618. }
  619. #endif