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.

635 lines
18 KiB

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