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.

644 lines
18 KiB

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